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
feat(spec): reject a body on a non-script action — it would never run (#3530) (#3548)
`Action.body` is documented as "only meaningful when `type === 'script'`", but
nothing enforced it. A `type: 'modal'` action authored with `params` and a
`body` — expecting the modal to collect the input and the body to write the
record on submit — passed validation, passed shape tests, and shipped a button
that opened a modal and silently wrote nothing. Non-script types all dispatch on
`target`, so there is no point at which a renderer would invoke the body.
Enforced the same way as the existing rule rejecting a `script` action with
neither `body` nor `target` (#2169): a parse-time error naming the fix.
Also updates content/docs/ui/actions.mdx and
content/docs/protocol/objectui/actions.mdx to document the rule and the
page → object → server-handler resolution order for a modal `target`.
Copy file name to clipboardExpand all lines: content/docs/protocol/objectui/actions.mdx
+4-2Lines changed: 4 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -55,7 +55,7 @@ The `type` field selects how an action is dispatched. The complete enum is **`sc
55
55
56
56
### Script Actions
57
57
58
-
Run logic with no server round trip via `body` (an L1 CEL expression or L2 sandboxed JS). `body` is only meaningful when `type` is `script`.
58
+
Run logic with no server round trip via `body` (an L1 CEL expression or L2 sandboxed JS). `body` is only meaningful when `type` is `script`, and declaring one on any other type is a parse-time error — those types dispatch on `target`, so the body would never be invoked.
59
59
60
60
```yaml
61
61
name: greet_user
@@ -142,7 +142,7 @@ target: customer_quick_edit
142
142
143
143
### Modal Actions
144
144
145
-
Open a modal/page by name:
145
+
Open a modal/page by name. `target` is resolved as a **page** first, then as an object (which opens that object's create/edit form); when it names neither, the action falls through to its server-side handler.
146
146
147
147
```yaml
148
148
name: edit_customer
@@ -151,6 +151,8 @@ type: modal
151
151
target: customer_edit_modal
152
152
```
153
153
154
+
A modal action may also declare `params` — the renderer collects them in a dialog before opening the target. It may **not** declare a `body`: see [Script Actions](#script-actions) above, and use `type: script` when the goal is to collect input and then run logic.
"A 'script' action requires either an inline `body` (sandboxed L1/L2 handler) or a `target` (a registered bundle function name).",
605
605
path: ['body'],
606
+
}).refine((data)=>{
607
+
// The mirror image of the rule above: a `body` on a NON-script action never
608
+
// runs. `type: 'modal' | 'url' | 'flow' | 'api' | 'form'` all dispatch on
609
+
// `target` (the page to open, the URL, the flow, the endpoint), so the
610
+
// renderer has no point at which it would invoke a body — the action opens
611
+
// its target and the body is silently skipped.
612
+
//
613
+
// This is the same invisible-failure shape as #2169: it passes build, passes
614
+
// shape tests, and only shows up as "the modal opened but nothing was
615
+
// written" (#3530, where `type: 'modal'` + `params` + `body` was authored
616
+
// expecting the body to run on submit). Reject it at author time and name the
617
+
// fix — `type: 'script'` collects the same `params` and DOES run the body.
618
+
if(data.type!=='script'&&data.body){
619
+
returnfalse;
620
+
}
621
+
returntrue;
622
+
},{
623
+
message:
624
+
"`body` only runs for `type: 'script'` — a non-script action dispatches on `target` and silently ignores its body. "+
625
+
"To collect `params` and then run the body, use `type: 'script'`; to open a page/modal, drop the `body` and keep `type: 'modal'` with `target` naming the page.",
626
+
path: ['body'],
606
627
}).refine((data)=>{
607
628
// ADR-0011: an exposed action must carry an LLM-facing description.
0 commit comments