Found while implementing objectui#6252 (PR objectui#7180). Filed rather than ridden: unifying the two copies changes behaviour on a renderer that card does not own.
The state after objectui#6252
Two renderers now resolve action ids against the object's own actions metadata, through the same useMetadataItem('object', name) entry, with the same byName map keyed on ActionDef.name, each written out inline:
packages/plugin-detail/src/renderers/record-quick-actions.tsx — namesToResolve / needsLookup / authoredActionspackages/components/src/renderers/layout/containers.tsx — headerActionIds / needsActionLookup / resolvedHeaderActions
Same mechanism, two hand-written copies. That is the shape objectui#5935 is filed about for the lucide icon resolvers, one class down.
The measured divergence
They already disagree on one input, deliberately and documented in the header's comment, but the divergence is real and is the thing that will drift:
record:quick_actions switches on the WHOLE array: rawActions.every((a) => typeof a === 'string'). A mixed ['convert', { … }] array therefore takes the object path and the string is passed to the renderer as an ActionDef, where it renders nothing.page:header normalises PER ELEMENT: the same mixed array resolves the id and passes the object through.
So during the migration objectui#6252 opens, one authored array means two different things depending on which of the two renderers draws it — which is the same class of defect objectstack#11592 was filed about, one level down.
Why it was not fixed in objectui#7180
Making the header's per-element rule the shared one changes record:quick_actions for mixed arrays — a behaviour change in a renderer objectui#6252 does not own, and one that wants a ruling rather than a developer's judgement (is a mixed array a migration state to support, or metadata to reject?). Making the whole-array rule the shared one would remove the header tolerance objectui#6252's card explicitly asked for.
Suggested shape
A pure resolveDeclaredActionIds(elements, registeredActions) in @object-ui/types (zero-dep, already the home of actionRendersAt, the other shared action predicate), called from both renderers — after the mixed-array semantics are settled one way for both.
Found while implementing objectui#6252 (PR objectui#7180). Filed rather than ridden: unifying the two copies changes behaviour on a renderer that card does not own.
The state after objectui#6252
Two renderers now resolve action ids against the object's own
actionsmetadata, through the sameuseMetadataItem('object', name)entry, with the samebyNamemap keyed onActionDef.name, each written out inline:packages/plugin-detail/src/renderers/record-quick-actions.tsx—namesToResolve/needsLookup/authoredActionspackages/components/src/renderers/layout/containers.tsx—headerActionIds/needsActionLookup/resolvedHeaderActionsSame mechanism, two hand-written copies. That is the shape objectui#5935 is filed about for the lucide icon resolvers, one class down.
The measured divergence
They already disagree on one input, deliberately and documented in the header's comment, but the divergence is real and is the thing that will drift:
record:quick_actionsswitches on the WHOLE array:rawActions.every((a) => typeof a === 'string'). A mixed['convert', { … }]array therefore takes the object path and the string is passed to the renderer as an ActionDef, where it renders nothing.page:headernormalises PER ELEMENT: the same mixed array resolves the id and passes the object through.So during the migration objectui#6252 opens, one authored array means two different things depending on which of the two renderers draws it — which is the same class of defect objectstack#11592 was filed about, one level down.
Why it was not fixed in objectui#7180
Making the header's per-element rule the shared one changes
record:quick_actionsfor mixed arrays — a behaviour change in a renderer objectui#6252 does not own, and one that wants a ruling rather than a developer's judgement (is a mixed array a migration state to support, or metadata to reject?). Making the whole-array rule the shared one would remove the header tolerance objectui#6252's card explicitly asked for.Suggested shape
A pure
resolveDeclaredActionIds(elements, registeredActions)in@object-ui/types(zero-dep, already the home ofactionRendersAt, the other shared action predicate), called from both renderers — after the mixed-array semantics are settled one way for both.