Skip to content

Commit 6e56f2f

Browse files
committed
docs(automation): a builtin's undeclared config key is refused at the flow parse; try_catch and plugin types stay on registerFlow's walk
Claude-Session: https://claude.ai/code/session_01DhTqaEHqPVSVnAkjG3jywn Co-authored-by: Claude <noreply@anthropic.com>
1 parent 789b3d5 commit 6e56f2f

1 file changed

Lines changed: 17 additions & 7 deletions

File tree

‎content/docs/automation/flows.mdx‎

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Each node performs a specific action in the flow.
138138
| `id` | `string` | ✅ | Unique node identifier — unique across the **whole flow**: the top-level `nodes[]` and every region body (`loop.body`, `parallel.branches[]`, `try_catch.try` / `.catch`, at any depth) share one id space, and `FlowSchema` refuses a reused id at parse (`Duplicate node id …`, naming both locations) |
139139
| `type` | `string` | ✅ | Node type — a built-in id from the table above **or** a plugin-registered one. Per ADR-0018 the spec does not gate this with a closed enum; it is checked against the live action registry once that registry is complete — plugins contribute node types while they start, so flows registered during boot are checked in one pass when the vocabulary closes (all plugins started), and anything registered after that (Studio publish, dev reload) is checked immediately. Unknown types warn, never reject; executing one fails with `NO_EXECUTOR` |
140140
| `label` | `string` | ✅ | Display label |
141-
| `config` | `object` | optional | Type-specific configuration — the registered executor's `configSchema` owns its shape. Keys that schema does not declare are rejected at `registerFlow()`, an `approval` node's config is judged whole against its declared contract at the flow parse, and the built-in executors `parse()` the value against their Zod contract before running (#4277) |
141+
| `config` | `object` | optional | Type-specific configuration — its node type owns its shape. On a built-in node type with an executor contract other than `try_catch`, a key that contract does not declare is refused at the flow parse, at the key's path; on `try_catch` and on a plugin node type, a key the published `configSchema` does not declare is rejected at `registerFlow()`. An `approval` node's config is judged whole against its declared contract at the flow parse, and the built-in executors `parse()` the value against their Zod contract before running |
142142
| `connectorConfig` | `object` | ✅ on `connector_action` | `{ connectorId, actionId, input }` — the only input a `connector_action` node's executor reads. `FlowSchema` refuses a `connector_action` node without it, and one whose `connectorId` or `actionId` is blank (empty or whitespace only), at any depth including a region body. `connectorId` is the registered connector's `name`, `actionId` one of the action keys it declares; `input` is optional |
143143
| `position` | `{ x, y }` | optional | Visual position on canvas |
144144
| `timeoutMs` | `number` | optional | Per-node execution timeout |
@@ -149,13 +149,23 @@ Each node performs a specific action in the flow.
149149
<Callout type="info">
150150
The flow, node, edge, and variable **shells are `.strict()`** — a key they do not
151151
declare is a parse error naming the likely intended key, not a silent strip. A
152-
node's `config` is open at the flow-parse level (plugin node types keep their own
153-
vocabulary), but it is not unchecked: `registerFlow()` rejects config keys the
154-
node type's published `configSchema` does not declare — naming the path, the
155-
declared key set, and a did-you-mean — and the contract-carrying builtins
152+
node's `config` has no single shape at the flow-parse level (each node type owns
153+
its vocabulary), but it is not unchecked. On every built-in node type with an
154+
executor contract except `try_catch` — `get_record`, `create_record`,
155+
`update_record`, `delete_record`, `notify`, `http`, `screen`, `map`, `loop`,
156+
`parallel`, `script` and `subflow` — the flow parse refuses a config key that
157+
contract does not declare, at the key's own path and in the contract's words
158+
(its did-you-mean, or its prescription for a known slip such as `fieldValues` →
159+
`fields`), so `objectstack validate`, `objectstack compile`, the metadata save
160+
door and `registerFlow()` all refuse it before anything runs (a key on a `loop`
161+
body or a `parallel` branch, or on a region's nodes and edges, is the region
162+
check's at `registerFlow()`). A `try_catch` node
163+
and a plugin node type are judged at `registerFlow()` instead, against the
164+
`configSchema` the node type publishes — naming the path, the declared key set,
165+
and a did-you-mean; a plugin node type that publishes no `configSchema` declares
166+
nothing, so nothing can be undeclared. The contract-carrying builtins
156167
additionally `parse()` their config at execute time, refusing the node on a type
157-
or missing-`required` violation (#4277). A node type that publishes no
158-
`configSchema` declares nothing, so nothing can be undeclared. An `approval`
168+
or missing-`required` violation. An `approval`
159169
node's config is judged whole by the flow parse, against the contract the spec
160170
declares for it: an undeclared key or a value its executor would refuse
161171
(`escalation.timeoutHours: 0.5` under its `>= 1`) refuses the flow at the

0 commit comments

Comments
 (0)