Skip to content

objectui: bodyShape is read by the console apiHandler but forwarded by ZERO action renderers — a declared wrap is silently dropped #6938

Description

@yinlianghui

Found while implementing objectstack#6837 (bodyExtra forwarding, objectui PR objectstack-ai/objectui#3924). Not fixed there — different key, and #6837's scope was bodyExtra.

The defect

bodyShape is the spec's body-wrapping declaration for a type: 'api' action — 'flat' (default) or { wrap: key } to nest the collected params under a key. The console apiHandlerreads it, unconditionally, on its absolute-HTTP branch:

packages/app-shell/src/hooks/useConsoleActionRuntime.tsx:277

const wrap = action.bodyShape && typeof action.bodyShape === 'object' && action.bodyShape.wrap
? action.bodyShape.wrap
: undefined;
const body = wrap ? { [wrap]: resolvedParams } : { ...resolvedParams };

But no action renderer forwards it. Measured on objectui origin/main @ 99ba5fbd (counter-probe: confirmText is present in all five whitelists, so the greps are live):

rendererforwards bodyShape?
packages/components/src/renderers/basic/elements.tsx (element:button)no
packages/components/src/renderers/action/action-button.tsxno
packages/components/src/renderers/action/action-group.tsxno
packages/components/src/renderers/action/action-icon.tsxno
packages/components/src/renderers/action/action-menu.tsxno

So an author who declares bodyShape: { wrap: 'data' } on an action rendered by any of these gets a flat body instead of a wrapped one — the endpoint receives the params at the top level and the declaration reads as honoured because it parses and publishes.

Reachability

Reachable on the declared-action renderers (action:button and friends), which is where bodyShape is authorable. It is NOT reachable through DeclaredActionsBar / RelatedRecordActionsBridge / ObjectGrid: those spread the whole ActionDef ({ params: rawParams, ...rest }) and carry the key through — which is exactly why the gap survived, since the record-header path works.

Note element:button is arguably correct to omit it: spec's InlineActionSchema pick list does not include bodyShape, so it is not inline vocabulary. The defect is the four declared-action renderers.

Sibling key, checked and NOT a defect

recordIdParam is also forwarded by none of the five, but the apiHandler only reads it under if (rowRecord && action.recordIdParam), and rowRecord arrives only from the spread-based hosts above — which also carry recordIdParam. So that one is unreachable, not broken. Recorded here so the next reader does not re-derive it.

Why this class keeps recurring

The explicit forward whitelist is a deliberate design (a key no renderer honours must not look wired), but it makes every NEW spec key invisible until five separate lists are edited, with nothing failing when they are not. bodyExtra was the same shape (#6837) and openIn / resultDialog / undoable / locations each have an in-comment note recording their own instance of it. Worth considering a pin test that diffs the renderers' forwarded key sets against the spec keys the runtime actually reads, so the next key fails loudly instead of silently.

Filed unassigned for triage.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions