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
fix(spec,service-automation): reconcile notify/http/connector declared config with what the executors read (#4045)
The notify/http/connector step of the declared-vs-read worklist:
- notify/http gain executor-derived Zod contracts (NotifyConfigSchema,
HttpConfigSchema) written from the executors, not the form literals,
plus a bidirectional form <-> Zod key-set ledger test. Both reconcile
clean, with no deliberately-shallow entries.
- connector_action's configSchema is retired: the executor reads only
the declared FlowNodeSchema.connectorConfig sibling, but the published
schema declared the trio as config keys - and the schema-driven Studio
form roots every field at config.<key>, replacing the hand-written
connectorConfig form, so online authoring produced nodes that refused
to dispatch. Schemaless drops the designer back onto the correct form.
- flow-node-connector-config-lift (ADR-0087 D2, protocol 17) heals
stored flows carrying the mis-taught config.{connectorId,actionId,
input} shape, with a completeness guard so a step-time refusal never
becomes a load failure.
- connectorConfig.input is optional, matching the executor (input ?? {})
and the designer's keyValue editor, which omits an empty map.
- check:variant-docs (new in #4177) is classified in the check:generated
ledger (NO_GENERATOR) - unclassified, it failed every gate run.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UDhMtxPLLoFaGtdpNA7xTU
Copy file name to clipboardExpand all lines: content/docs/references/automation/flow.mdx
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -99,7 +99,7 @@ const result = Flow.parse(data);
99
99
|**type**|`string`| ✅ | Action type — a built-in FlowNodeAction id or a plugin-registered node type. Validated against the live action registry at registerFlow() (ADR-0018), not by a closed enum. |
|**severity**|`string`| optional | info \| warning \| critical |
97
+
|**sourceObject**|`string`| optional | Object name of the record the notification links to (writes sys_notification.source_object). Requires sourceId. |
98
+
|**sourceId**|`string`| optional | Record id the notification links to (writes sys_notification.source_id). Requires sourceObject. |
99
+
|**actorId**|`string`| optional | User id that caused the event (writes sys_notification.actor_id) |
100
+
|**actionUrl**|`string`| optional | Explicit click-through URL; overrides the link synthesized from sourceObject/sourceId |
101
+
|**payload**|`Record<string, any>`| optional | Extra template inputs merged into the notification payload |
Copy file name to clipboardExpand all lines: docs/protocol-upgrade-guide.md
+3Lines changed: 3 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -126,6 +126,8 @@ Beyond those spec-surface removals, it graduates the seven flow-node config key
126
126
127
127
The same graduation covers `wait`, whose fallback was not a config-to-config rename (#4045). `wait` keeps its contract in the declared `waitEventConfig` block, not in `config` at all — yet the executor also read six loose `config` keys, two of them (`duration`, `signal`) spellings the spec never declared. The conversion lifts them onto the declared block in the executor's own `??` precedence, so a value already declared wins and its loose counterpart is left shadowed. One wrinkle makes this a rewrite rather than a delete: `waitEventConfig.eventType` is required once the block exists, and the loader parses the CONVERTED flow — so a source carrying only `config: { duration }` is stamped with `eventType: 'timer'`, the exact default the executor applied to that shape. Behaviour-preserving in both directions.
128
128
129
+
`connector_action` gets the same lift for the opposite reason (#4045). Its contract also lives in a declared sibling block (`connectorConfig`), and the executor never read `config` at all — but the node's descriptor published a `configSchema` declaring `connectorId`/`actionId`/`input` as `config` keys, and the Studio inspector derives its form from a published schema, so schema-driven authoring wrote the trio to the wrong place and produced nodes that refused to dispatch. The conversion lifts the trio onto the declared block (declared keys win; a lift that cannot complete the required connectorId+actionId pair leaves the node untouched rather than turning a step-time refusal into a load failure), and the descriptor stops publishing the mis-rooted schema.
130
+
129
131
And it removes the RLS-policy key `priority` (#3896 security audit): promised "conflict resolution" that cannot exist, because applicable policies OR-combine (most permissive wins) — there is never a conflict to order, and nothing ever read the key (call graph closed across the collection site, the projection round-trip and the compiler). A pure lossless delete: outcomes are identical with or without it; the schema tombstones the key with the same prescription.
130
132
131
133
The same close-out retires the four inert tool authoring keys (`category`, `permissions`, `active`, `builtIn`): none is part of AIToolDefinition and no execution path read them. Two were misleading in the dangerous direction — `permissions` promised an invocation gate nothing enforced, and `active: false` read as "withdrawn" while the tool kept reaching the LLM tool set. Lossless deletes; the strict ToolSchema rejects each with its prescription.
@@ -149,6 +151,7 @@ The close-out sweep finishes the enforce-or-remove worklist across the remaining
149
151
|`flow-node-crud-object-alias`|`flow.node.config.objectName`| CRUD flow-node config key 'object' → 'objectName' (#3796 — `readAliasedConfig` shim graduation) | live — protocol 17 loader accepts the old shape |
150
152
|`flow-node-notify-config-aliases`|`flow.node.notify.config`| notify flow-node config keys 'to' → 'recipients', 'subject' → 'title', 'body' → 'message', 'url' → 'actionUrl' (#3796), and nested 'source: {object, id}' → 'sourceObject' / 'sourceId' (#4045) | live — protocol 17 loader accepts the old shape |
151
153
|`flow-node-wait-event-config-lift`|`flow.node.wait.waitEventConfig`| wait flow-node loose config keys → the declared `waitEventConfig` block: 'eventType', 'timerDuration'/'duration' → 'timerDuration', 'signalName'/'signal' → 'signalName', 'timeoutMs' (#4045) | live — protocol 17 loader accepts the old shape |
154
+
|`flow-node-connector-config-lift`|`flow.node.connector_action.connectorConfig`| connector_action flow-node loose config keys 'connectorId' / 'actionId' / 'input' → the declared `connectorConfig` block (#4045) | live — protocol 17 loader accepts the old shape |
152
155
|`flow-node-script-config-aliases`|`flow.node.script.config`| script flow-node config keys 'functionName' → 'function', 'input' → 'inputs' (#3796) | live — protocol 17 loader accepts the old shape |
153
156
|`permission-rls-priority-removed`|`permission.rowLevelSecurity.priority`| RLS-policy key 'priority' removed (#3896 audit — policies OR-combine, so the promised conflict-resolution semantics cannot exist; dropping it changes no outcome) | retired — `migrate meta` only |
0 commit comments