@uipath/skills 1.204.0 → 1.204.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CODEOWNERS +12 -14
- package/package.json +1 -1
- package/skills/uipath-agents/references/coded/embedding-in-flows.md +1 -3
- package/skills/uipath-agents/references/coded/quickstart.md +0 -2
- package/skills/uipath-agents/references/lowcode/capabilities/inline-in-flow/inline-in-flow.md +10 -8
- package/skills/uipath-agents/references/lowcode/prompting/autonomous-agent-prompting-guide.md +1 -1
- package/skills/uipath-human-in-the-loop/SKILL.md +1 -3
- package/skills/uipath-human-in-the-loop/references/hitl-node-apptask.md +0 -2
- package/skills/uipath-human-in-the-loop/references/hitl-node-coded-action-app.md +0 -2
- package/skills/uipath-human-in-the-loop/references/hitl-node-quickform.md +2 -2
- package/skills/uipath-maestro-flow/.maintenance/README.md +265 -0
- package/skills/uipath-maestro-flow/.maintenance/check-all.sh +40 -0
- package/skills/uipath-maestro-flow/.maintenance/check-anchors.sh +101 -0
- package/skills/uipath-maestro-flow/.maintenance/check-depth.sh +132 -0
- package/skills/uipath-maestro-flow/.maintenance/check-link-text.sh +123 -0
- package/skills/uipath-maestro-flow/.maintenance/check-links.sh +73 -0
- package/skills/uipath-maestro-flow/.maintenance/check-orphans.sh +93 -0
- package/skills/uipath-maestro-flow/.maintenance/check-plugin-pairs.sh +39 -0
- package/skills/uipath-maestro-flow/.maintenance/check-template.sh +42 -0
- package/skills/uipath-maestro-flow/.maintenance/check-uip-commands.sh +270 -0
- package/skills/uipath-maestro-flow/.maintenance/check-versions.sh +160 -0
- package/skills/uipath-maestro-flow/SKILL.md +69 -886
- package/skills/uipath-maestro-flow/references/author/CAPABILITY.md +207 -0
- package/skills/uipath-maestro-flow/references/author/brownfield.md +91 -0
- package/skills/uipath-maestro-flow/references/author/editing-operations-cli.md +190 -0
- package/skills/uipath-maestro-flow/references/author/editing-operations-json.md +374 -0
- package/skills/uipath-maestro-flow/references/author/editing-operations.md +104 -0
- package/skills/uipath-maestro-flow/references/author/greenfield.md +420 -0
- package/skills/uipath-maestro-flow/references/author/planning-arch.md +364 -0
- package/skills/uipath-maestro-flow/references/author/planning-impl.md +291 -0
- package/skills/uipath-maestro-flow/references/author/plugins/agent/impl.md +237 -0
- package/skills/uipath-maestro-flow/references/author/plugins/agent/planning.md +97 -0
- package/skills/uipath-maestro-flow/references/author/plugins/agentic-process/impl.md +107 -0
- package/skills/uipath-maestro-flow/references/author/plugins/agentic-process/planning.md +61 -0
- package/skills/uipath-maestro-flow/references/author/plugins/api-workflow/impl.md +108 -0
- package/skills/uipath-maestro-flow/references/author/plugins/api-workflow/planning.md +60 -0
- package/skills/uipath-maestro-flow/references/author/plugins/batch-transform/impl.md +171 -0
- package/skills/uipath-maestro-flow/references/author/plugins/batch-transform/planning.md +72 -0
- package/skills/uipath-maestro-flow/references/author/plugins/connector/impl-inline.md +120 -0
- package/skills/uipath-maestro-flow/references/author/plugins/connector/impl.md +463 -0
- package/skills/uipath-maestro-flow/references/author/plugins/connector/planning.md +186 -0
- package/skills/uipath-maestro-flow/references/author/plugins/connector-trigger/impl.md +517 -0
- package/skills/uipath-maestro-flow/references/author/plugins/connector-trigger/planning.md +115 -0
- package/skills/uipath-maestro-flow/references/author/plugins/conversational-agent/impl.md +309 -0
- package/skills/uipath-maestro-flow/references/author/plugins/conversational-agent/planning.md +133 -0
- package/skills/uipath-maestro-flow/references/author/plugins/data-fabric/impl.md +428 -0
- package/skills/uipath-maestro-flow/references/author/plugins/data-fabric/planning.md +188 -0
- package/skills/uipath-maestro-flow/references/author/plugins/decision/impl.md +83 -0
- package/skills/uipath-maestro-flow/references/author/plugins/decision/planning.md +38 -0
- package/skills/uipath-maestro-flow/references/author/plugins/delay/impl.md +38 -0
- package/skills/uipath-maestro-flow/references/author/plugins/delay/planning.md +53 -0
- package/skills/uipath-maestro-flow/references/author/plugins/end/impl.md +56 -0
- package/skills/uipath-maestro-flow/references/author/plugins/end/planning.md +28 -0
- package/skills/uipath-maestro-flow/references/author/plugins/flow/impl.md +107 -0
- package/skills/uipath-maestro-flow/references/author/plugins/flow/planning.md +58 -0
- package/skills/uipath-maestro-flow/references/author/plugins/hitl/impl.md +181 -0
- package/skills/uipath-maestro-flow/references/author/plugins/hitl/planning.md +166 -0
- package/skills/uipath-maestro-flow/references/author/plugins/http/impl-connector.md +86 -0
- package/skills/uipath-maestro-flow/references/author/plugins/http/impl-manual.md +68 -0
- package/skills/uipath-maestro-flow/references/author/plugins/http/impl.md +173 -0
- package/skills/uipath-maestro-flow/references/author/plugins/http/planning.md +107 -0
- package/skills/uipath-maestro-flow/references/author/plugins/inline-agent/impl.md +540 -0
- package/skills/uipath-maestro-flow/references/author/plugins/inline-agent/planning.md +115 -0
- package/skills/uipath-maestro-flow/references/author/plugins/inline-voice-agent/impl.md +269 -0
- package/skills/uipath-maestro-flow/references/author/plugins/inline-voice-agent/planning.md +128 -0
- package/skills/uipath-maestro-flow/references/author/plugins/ixp/impl.md +424 -0
- package/skills/uipath-maestro-flow/references/author/plugins/ixp/planning.md +95 -0
- package/skills/uipath-maestro-flow/references/author/plugins/loop/impl.md +403 -0
- package/skills/uipath-maestro-flow/references/author/plugins/loop/planning.md +60 -0
- package/skills/uipath-maestro-flow/references/author/plugins/merge/impl.md +43 -0
- package/skills/uipath-maestro-flow/references/author/plugins/merge/planning.md +24 -0
- package/skills/uipath-maestro-flow/references/author/plugins/queue/impl.md +82 -0
- package/skills/uipath-maestro-flow/references/author/plugins/queue/planning.md +44 -0
- package/skills/uipath-maestro-flow/references/author/plugins/rpa/impl.md +117 -0
- package/skills/uipath-maestro-flow/references/author/plugins/rpa/planning.md +61 -0
- package/skills/uipath-maestro-flow/references/author/plugins/scheduled-trigger/impl.md +56 -0
- package/skills/uipath-maestro-flow/references/author/plugins/scheduled-trigger/planning.md +93 -0
- package/skills/uipath-maestro-flow/references/author/plugins/script/impl.md +98 -0
- package/skills/uipath-maestro-flow/references/author/plugins/script/planning.md +42 -0
- package/skills/uipath-maestro-flow/references/author/plugins/subflow/impl.md +164 -0
- package/skills/uipath-maestro-flow/references/author/plugins/subflow/planning.md +35 -0
- package/skills/uipath-maestro-flow/references/author/plugins/summarize/impl.md +189 -0
- package/skills/uipath-maestro-flow/references/author/plugins/summarize/planning.md +66 -0
- package/skills/uipath-maestro-flow/references/author/plugins/switch/impl.md +72 -0
- package/skills/uipath-maestro-flow/references/author/plugins/switch/planning.md +34 -0
- package/skills/uipath-maestro-flow/references/author/plugins/terminate/impl.md +48 -0
- package/skills/uipath-maestro-flow/references/author/plugins/terminate/planning.md +29 -0
- package/skills/uipath-maestro-flow/references/author/plugins/transform/impl.md +252 -0
- package/skills/uipath-maestro-flow/references/author/plugins/transform/planning.md +47 -0
- package/skills/uipath-maestro-flow/references/diagnose/CAPABILITY.md +73 -0
- package/skills/uipath-maestro-flow/references/diagnose/failure-modes.md +353 -0
- package/skills/uipath-maestro-flow/references/diagnose/troubleshooting-guide.md +174 -0
- package/skills/uipath-maestro-flow/references/evaluate/CAPABILITY.md +134 -0
- package/skills/uipath-maestro-flow/references/evaluate/commands-reference.md +376 -0
- package/skills/uipath-maestro-flow/references/evaluate/eval-sets-guide.md +227 -0
- package/skills/uipath-maestro-flow/references/evaluate/evaluators-guide.md +143 -0
- package/skills/uipath-maestro-flow/references/evaluate/running-guide.md +215 -0
- package/skills/uipath-maestro-flow/references/evaluate/upload-safety.md +94 -0
- package/skills/uipath-maestro-flow/references/operate/CAPABILITY.md +77 -0
- package/skills/uipath-maestro-flow/references/operate/manage.md +39 -0
- package/skills/uipath-maestro-flow/references/operate/run.md +121 -0
- package/skills/uipath-maestro-flow/references/operate/ship.md +57 -0
- package/skills/uipath-maestro-flow/references/shared/action-nodes.md +100 -0
- package/skills/uipath-maestro-flow/references/shared/cli-commands.md +269 -0
- package/skills/uipath-maestro-flow/references/shared/cli-conventions.md +178 -0
- package/skills/uipath-maestro-flow/references/shared/file-format.md +582 -0
- package/skills/uipath-maestro-flow/references/shared/node-output-wiring.md +86 -0
- package/skills/uipath-maestro-flow/references/shared/ux-narration-and-todos.md +113 -0
- package/skills/uipath-maestro-flow/references/shared/variables-and-expressions.md +787 -0
- package/skills/uipath-platform/references/integration-service/activities.md +2 -2
- package/skills/uipath-platform/references/integration-service/activity-generation.md +8 -1
- package/skills/uipath-platform/references/integration-service/triggers.md +1 -3
- package/version-manifest.json +1 -1
- package/skills/uipath-maestro-flow/examples/AquariumSafetyStop.flow.ts +0 -26
- package/skills/uipath-maestro-flow/examples/ArchiveCardExtract.flow.ts +0 -38
- package/skills/uipath-maestro-flow/examples/BakeOffEntryForm.flow.ts +0 -27
- package/skills/uipath-maestro-flow/examples/BeeHiveLedger.flow.ts +0 -61
- package/skills/uipath-maestro-flow/examples/BeltProgression.flow.ts +0 -53
- package/skills/uipath-maestro-flow/examples/BirdCountLookup.flow.ts +0 -24
- package/skills/uipath-maestro-flow/examples/ClubDirectory.flow.ts +0 -56
- package/skills/uipath-maestro-flow/examples/ConcertSoundcheck.flow.ts +0 -59
- package/skills/uipath-maestro-flow/examples/DoorbellLog.flow.ts +0 -45
- package/skills/uipath-maestro-flow/examples/FestivalMapScaffold.flow.ts +0 -22
- package/skills/uipath-maestro-flow/examples/FieldTripQuickForm.flow.ts +0 -45
- package/skills/uipath-maestro-flow/examples/FossilCatalogEnrich.flow.ts +0 -26
- package/skills/uipath-maestro-flow/examples/GallerySubmission.flow.ts +0 -67
- package/skills/uipath-maestro-flow/examples/GreenhouseWatering.flow.ts +0 -39
- package/skills/uipath-maestro-flow/examples/HarbourRadioLine.flow.ts +0 -31
- package/skills/uipath-maestro-flow/examples/HerbariumDispatch.flow.ts +0 -34
- package/skills/uipath-maestro-flow/examples/KilnReview.flow.ts +0 -38
- package/skills/uipath-maestro-flow/examples/LibraryDeskChat.flow.ts +0 -39
- package/skills/uipath-maestro-flow/examples/LighthouseSignal.flow.ts +0 -30
- package/skills/uipath-maestro-flow/examples/MeteorShowerPages.flow.ts +0 -39
- package/skills/uipath-maestro-flow/examples/NeighborhoodWalkPlanner.flow.ts +0 -27
- package/skills/uipath-maestro-flow/examples/ObservatorySeeing.flow.ts +0 -51
- package/skills/uipath-maestro-flow/examples/OralHistoryDigest.flow.ts +0 -31
- package/skills/uipath-maestro-flow/examples/PlanetariumConfirmation.flow.ts +0 -34
- package/skills/uipath-maestro-flow/examples/PlantNameAdvisor.flow.ts +0 -28
- package/skills/uipath-maestro-flow/examples/PostcardCaption.flow.ts +0 -44
- package/skills/uipath-maestro-flow/examples/PotteryStudioCallback.flow.ts +0 -41
- package/skills/uipath-maestro-flow/examples/RecipeScaler.flow.ts +0 -62
- package/skills/uipath-maestro-flow/examples/SeedPacketReader.flow.ts +0 -49
- package/skills/uipath-maestro-flow/examples/TideTableConverter.flow.ts +0 -35
- package/skills/uipath-maestro-flow/examples/TrailLogSummary.flow.ts +0 -75
- package/skills/uipath-maestro-flow/examples/WorkshopInventory.flow.ts +0 -27
- package/skills/uipath-maestro-flow/examples/bindings.json +0 -85
- package/skills/uipath-maestro-flow/references/CLI-LOOP.md +0 -309
- package/skills/uipath-maestro-flow/references/agent-resources.md +0 -74
- package/skills/uipath-maestro-flow/references/agent.md +0 -175
- package/skills/uipath-maestro-flow/references/agentic-process.md +0 -41
- package/skills/uipath-maestro-flow/references/api-workflow.md +0 -29
- package/skills/uipath-maestro-flow/references/author.md +0 -178
- package/skills/uipath-maestro-flow/references/batch-transform.md +0 -23
- package/skills/uipath-maestro-flow/references/bindings.md +0 -178
- package/skills/uipath-maestro-flow/references/branch.md +0 -18
- package/skills/uipath-maestro-flow/references/brownfield.md +0 -152
- package/skills/uipath-maestro-flow/references/connector-params.md +0 -624
- package/skills/uipath-maestro-flow/references/conversational.md +0 -77
- package/skills/uipath-maestro-flow/references/data-fabric.md +0 -268
- package/skills/uipath-maestro-flow/references/delay.md +0 -26
- package/skills/uipath-maestro-flow/references/diagnose.md +0 -141
- package/skills/uipath-maestro-flow/references/document-pipeline.md +0 -47
- package/skills/uipath-maestro-flow/references/error-handling.md +0 -75
- package/skills/uipath-maestro-flow/references/evaluate.md +0 -168
- package/skills/uipath-maestro-flow/references/event-trigger.md +0 -155
- package/skills/uipath-maestro-flow/references/form-trigger.md +0 -41
- package/skills/uipath-maestro-flow/references/hitl.md +0 -169
- package/skills/uipath-maestro-flow/references/http.md +0 -105
- package/skills/uipath-maestro-flow/references/inline-agent.md +0 -132
- package/skills/uipath-maestro-flow/references/ixp.md +0 -167
- package/skills/uipath-maestro-flow/references/loops.md +0 -77
- package/skills/uipath-maestro-flow/references/manual-trigger.md +0 -39
- package/skills/uipath-maestro-flow/references/operate.md +0 -204
- package/skills/uipath-maestro-flow/references/or-processes.md +0 -91
- package/skills/uipath-maestro-flow/references/parallel-merge.md +0 -21
- package/skills/uipath-maestro-flow/references/placeholder.md +0 -70
- package/skills/uipath-maestro-flow/references/published-function.md +0 -66
- package/skills/uipath-maestro-flow/references/queue.md +0 -53
- package/skills/uipath-maestro-flow/references/return.md +0 -20
- package/skills/uipath-maestro-flow/references/rpa-workflow.md +0 -27
- package/skills/uipath-maestro-flow/references/scheduled-trigger.md +0 -34
- package/skills/uipath-maestro-flow/references/script.md +0 -52
- package/skills/uipath-maestro-flow/references/subflow.md +0 -33
- package/skills/uipath-maestro-flow/references/summarize.md +0 -20
- package/skills/uipath-maestro-flow/references/switch.md +0 -20
- package/skills/uipath-maestro-flow/references/terminate.md +0 -22
- package/skills/uipath-maestro-flow/references/transform.md +0 -32
- package/skills/uipath-maestro-flow/references/voice.md +0 -70
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# Loop Node — Implementation
|
|
2
|
+
|
|
3
|
+
## Node Type
|
|
4
|
+
|
|
5
|
+
`core.logic.loop`
|
|
6
|
+
|
|
7
|
+
## Registry Validation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
uip maestro flow registry get core.logic.loop --output json
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Confirm the handles, required input `collection`, and the `outputDefinition` keys. Set the node instance `typeVersion` to the `version` field from this response — do not hardcode it.
|
|
14
|
+
|
|
15
|
+
The loop is a **container** node, so its handles split across two boundaries:
|
|
16
|
+
|
|
17
|
+
| Boundary | Handle | Direction | Purpose |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| outer | `input` | target | Entry from upstream |
|
|
20
|
+
| outer | `success` | source | Exit after all iterations complete |
|
|
21
|
+
| outer | `error` | source | Error path (when `errorHandlingEnabled`) |
|
|
22
|
+
| inner | `start` | source | Into the loop body (first body node) |
|
|
23
|
+
| inner | `continue` | target | Return from last body node |
|
|
24
|
+
| inner | `break` | target | Early exit (when `breakEnabled`) |
|
|
25
|
+
|
|
26
|
+
> There is no `output` port and no `loopBack` port. `output` is an output **variable** (`$vars.<loopId>.output`), not a handle. Wiring either name is a hard `flow validate` error — `Edge references undeclared source handle "output" on node "<loopId>" … rewire to one of: success, error, start`.
|
|
27
|
+
|
|
28
|
+
## JSON Structure
|
|
29
|
+
|
|
30
|
+
### Loop node
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"id": "loop1",
|
|
35
|
+
"type": "core.logic.loop",
|
|
36
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
37
|
+
"display": { "label": "Loop over items" },
|
|
38
|
+
"inputs": {
|
|
39
|
+
"collection": "=js:$vars.fetchData.output.body.items",
|
|
40
|
+
"parallel": false
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
> **Collection source shape depends on the node type.** Connector `list` activities return the bare array — `"collection": "=js:$vars.searchIssuesByJql1.output"`, NOT `.output.issues` / `.output.items`. HTTP nodes differ — envelope preserved: `.output.body.<key>` as above. See [connector/impl.md — Connector output shape](../connector/impl.md).
|
|
46
|
+
|
|
47
|
+
Set `"parallel": true` to execute all iterations concurrently.
|
|
48
|
+
|
|
49
|
+
### Loop body nodes — `parentId` required
|
|
50
|
+
|
|
51
|
+
Every node inside the loop body **must** have `"parentId"` set to the loop node's ID. Without this, the runtime does not know the node is part of the loop and variableUpdates will not fire per-iteration.
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"id": "processItem",
|
|
56
|
+
"type": "core.action.script",
|
|
57
|
+
"typeVersion": "1.0",
|
|
58
|
+
"display": { "label": "Process item" },
|
|
59
|
+
"inputs": {
|
|
60
|
+
"script": "return $vars.product * $vars.loop1.currentItem"
|
|
61
|
+
},
|
|
62
|
+
"parentId": "loop1"
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
> **Critical:** If you omit `parentId`, the node executes outside the loop context. State variables will not update across iterations and loop outputs like `currentItem` will be inaccessible.
|
|
67
|
+
|
|
68
|
+
## Adding / Editing
|
|
69
|
+
|
|
70
|
+
For step-by-step add, delete, and wiring procedures, see [editing-operations.md](../../editing-operations.md). Use the JSON structure above for the node-specific `inputs` and `parentId`.
|
|
71
|
+
|
|
72
|
+
### CLI carve-out nodes inside a loop
|
|
73
|
+
|
|
74
|
+
When adding a CLI carve-out node (connector, connector trigger, or managed HTTP) inside a loop, pass `--parent` to set `parentId` automatically:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
uip maestro flow node add <FLOW_PATH> core.action.http.v2 --label "Fetch data" --parent <LOOP_ID> --output json
|
|
78
|
+
uip maestro flow node add <FLOW_PATH> <CONNECTOR_NODE_TYPE> --label "Get record" --parent <LOOP_ID> --output json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`--parent` validates the parent node exists and sets `parentId` on the new node. For non-carve-out node types (script, end, etc.), set `"parentId"` directly in the JSON — see the examples above.
|
|
82
|
+
|
|
83
|
+
## Wiring
|
|
84
|
+
|
|
85
|
+
Loop nodes have a specific wiring pattern — four edges:
|
|
86
|
+
|
|
87
|
+
| Edge | Source | Source port | Target | Target port |
|
|
88
|
+
| --- | --- | --- | --- | --- |
|
|
89
|
+
| Enter the loop | upstream node | that node's own output port (`output` on a manual trigger, `success` on an action node) | `<loopId>` | `input` |
|
|
90
|
+
| Into the body | `<loopId>` | `start` | first body node | `input` |
|
|
91
|
+
| Back from the body | last body node | `success` | `<loopId>` | `continue` |
|
|
92
|
+
| Exit the loop | `<loopId>` | `success` | downstream node | `input` |
|
|
93
|
+
|
|
94
|
+
See [editing-operations.md](../../editing-operations.md) for edge add procedures.
|
|
95
|
+
|
|
96
|
+
## Accessing Loop Variables Inside Body
|
|
97
|
+
|
|
98
|
+
Inside the loop body, access the current item via `$vars.<loopId>.currentItem`:
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
// In a Script node inside the loop body (parentId must be set to the loop node)
|
|
102
|
+
const item = $vars.loop1.currentItem;
|
|
103
|
+
const iteration = $vars.loop1.currentIteration;
|
|
104
|
+
return { processed: item.name.toUpperCase(), position: iteration };
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
| Variable | Description |
|
|
108
|
+
| --- | --- |
|
|
109
|
+
| `$vars.<loopId>.currentItem` | The item being processed in this iteration |
|
|
110
|
+
| `$vars.<loopId>.currentIteration` | 1-based iteration number |
|
|
111
|
+
| `$vars.<loopId>.collection` | The full collection being iterated |
|
|
112
|
+
|
|
113
|
+
> **Do not use `iterator.currentItem`.** The correct access pattern is `$vars.<loopId>.currentItem` where `<loopId>` is the loop node's `id` (e.g., `$vars.loop1.currentItem`).
|
|
114
|
+
|
|
115
|
+
> **There is no `currentIndex`.** The registry's `outputDefinition` for `core.logic.loop` exposes `currentIteration`, and it starts at 1 — not 0. Nothing rejects the wrong name: a `variables.nodes` entry bound to `outputId: "currentIndex"` passes `flow validate`, because bindings are not checked against the node's declared outputs. Confirm the output names with `uip maestro flow registry get core.logic.loop --output json`.
|
|
116
|
+
|
|
117
|
+
### Required node variables for loop outputs
|
|
118
|
+
|
|
119
|
+
For `$vars.<loopId>.currentItem` etc. to resolve, you must add corresponding entries to `variables.nodes`:
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"variables": {
|
|
124
|
+
"nodes": [
|
|
125
|
+
{
|
|
126
|
+
"id": "loop1.currentItem",
|
|
127
|
+
"type": "any",
|
|
128
|
+
"description": "The current item being iterated in the loop",
|
|
129
|
+
"binding": { "nodeId": "loop1", "outputId": "currentItem" }
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"id": "loop1.currentIteration",
|
|
133
|
+
"type": "number",
|
|
134
|
+
"description": "The current iteration number (1-based)",
|
|
135
|
+
"binding": { "nodeId": "loop1", "outputId": "currentIteration" }
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "loop1.collection",
|
|
139
|
+
"type": "array",
|
|
140
|
+
"description": "The collection being iterated over",
|
|
141
|
+
"binding": { "nodeId": "loop1", "outputId": "collection" }
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"id": "loop1.output",
|
|
145
|
+
"type": "array",
|
|
146
|
+
"description": "Aggregated results from all loop iterations",
|
|
147
|
+
"binding": { "nodeId": "loop1", "outputId": "output" }
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Aggregated loop output (`$vars.<loopId>.output`)
|
|
155
|
+
|
|
156
|
+
After the loop completes, `$vars.<loopId>.output` is an array with **one entry per iteration**. Each entry is **keyed by body node id**, with that node's outputs nested underneath — it is NOT the body node's bare return value.
|
|
157
|
+
|
|
158
|
+
For a loop `multiplyNumbers` over `[13, 15, 17]` whose body node `multiplyCurrent` is a Script returning `{ number: <n> }`:
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
[
|
|
162
|
+
{ "multiplyCurrent": { "output": { "number": 13 } } },
|
|
163
|
+
{ "multiplyCurrent": { "output": { "number": 15 } } },
|
|
164
|
+
{ "multiplyCurrent": { "output": { "number": 17 } } }
|
|
165
|
+
]
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
So a downstream Script reads a per-iteration value as `item.<bodyNodeId>.output.<field>`:
|
|
169
|
+
|
|
170
|
+
```javascript
|
|
171
|
+
// CORRECT
|
|
172
|
+
return { product: $vars.multiplyNumbers.output.reduce((p, item) => p * item.multiplyCurrent.output.number, 1) };
|
|
173
|
+
|
|
174
|
+
// WRONG — item.number is undefined; 1 * undefined === NaN
|
|
175
|
+
return { product: $vars.multiplyNumbers.output.reduce((p, item) => p * item.number, 1) };
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
With multiple body nodes, every node that ran in that iteration appears as a sibling key (`{ "fetchData1": { "output": … }, "processItem1": { "output": … } }`), alongside `<bodyNodeId>.error`.
|
|
179
|
+
|
|
180
|
+
> **`flow validate` cannot catch this.** The wrong accessor is valid JS over an `any`-typed array — it validates clean and silently produces `NaN`/`undefined` at runtime. Confirm the shape with one `uip maestro flow debug` run before wiring downstream consumers.
|
|
181
|
+
|
|
182
|
+
## State Accumulation with variableUpdates
|
|
183
|
+
|
|
184
|
+
To accumulate state across loop iterations (counters, running totals), use an `inout` variable with a `variableUpdate` on the body node:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"variables": {
|
|
189
|
+
"globals": [
|
|
190
|
+
{
|
|
191
|
+
"id": "runningTotal",
|
|
192
|
+
"direction": "inout",
|
|
193
|
+
"type": "number",
|
|
194
|
+
"defaultValue": 0
|
|
195
|
+
}
|
|
196
|
+
],
|
|
197
|
+
"variableUpdates": {
|
|
198
|
+
"bodyNode": [
|
|
199
|
+
{
|
|
200
|
+
"variableId": "runningTotal",
|
|
201
|
+
"expression": {
|
|
202
|
+
"type": "jsExpression",
|
|
203
|
+
"expression": "$vars.bodyNode.output",
|
|
204
|
+
"fieldType": "number"
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
The variableUpdate fires after each iteration, so the `inout` variable carries the accumulated value into the next iteration.
|
|
214
|
+
|
|
215
|
+
> **`expression` is an object, not a `=js:` string.** `{ "type": "jsExpression", "expression": "<bare JS, no =js: prefix>", "fieldType": "<target variable's type>" }`. The legacy string form fails `flow validate` with `[MIGRATION] Workflow migration failed at 1.9→1.10 … Offending field(s): variables.variableUpdates.<nodeId>.0.expression` and `"Retry": "RetryWillNotFix"`. Write it with `Edit` — there is no CLI command for variable updates. See [shared/variables-and-expressions.md § Variable Updates](../../../shared/variables-and-expressions.md#variable-updates-variableupdates).
|
|
216
|
+
|
|
217
|
+
> **Critical:** The variableUpdate expression **cannot** access loop iteration variables like `$vars.<loopId>.currentItem`. These are only available inside the body node's script. The variableUpdate must reference the body node's output (e.g., `$vars.bodyNode.output`). If you need to compute using `currentItem`, do the computation in the script and reference the script's output in the variableUpdate.
|
|
218
|
+
|
|
219
|
+
## Complete Example — Loop with State Accumulation
|
|
220
|
+
|
|
221
|
+
A flow that iterates over a collection, accumulates a result in an `inout` variable via a Script body node, and outputs the final value.
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"nodes": [
|
|
226
|
+
{
|
|
227
|
+
"id": "start",
|
|
228
|
+
"type": "core.trigger.manual",
|
|
229
|
+
"typeVersion": "1.0",
|
|
230
|
+
"display": { "label": "Manual trigger" },
|
|
231
|
+
"inputs": { "entryPointId": "..." }
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
"id": "loop1",
|
|
235
|
+
"type": "core.logic.loop",
|
|
236
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
237
|
+
"display": { "label": "Loop" },
|
|
238
|
+
"inputs": { "collection": "=js:$vars.inputItems", "parallel": false }
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
"id": "bodyScript",
|
|
242
|
+
"type": "core.action.script",
|
|
243
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
244
|
+
"display": { "label": "Process item" },
|
|
245
|
+
"inputs": {
|
|
246
|
+
"script": "return $vars.accumulator + $vars.loop1.currentItem;"
|
|
247
|
+
},
|
|
248
|
+
"parentId": "loop1"
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
"id": "end1",
|
|
252
|
+
"type": "core.control.end",
|
|
253
|
+
"typeVersion": "1.0",
|
|
254
|
+
"display": { "label": "End" },
|
|
255
|
+
"inputs": {},
|
|
256
|
+
"outputs": {
|
|
257
|
+
"result": { "source": "=js:$vars.accumulator" }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
],
|
|
261
|
+
"edges": [
|
|
262
|
+
{ "id": "e1", "sourceNodeId": "start", "sourcePort": "output", "targetNodeId": "loop1", "targetPort": "input" },
|
|
263
|
+
{ "id": "e2", "sourceNodeId": "loop1", "sourcePort": "start", "targetNodeId": "bodyScript", "targetPort": "input" },
|
|
264
|
+
{ "id": "e3", "sourceNodeId": "bodyScript", "sourcePort": "success", "targetNodeId": "loop1", "targetPort": "continue" },
|
|
265
|
+
{ "id": "e4", "sourceNodeId": "loop1", "sourcePort": "success", "targetNodeId": "end1", "targetPort": "input" }
|
|
266
|
+
],
|
|
267
|
+
"variables": {
|
|
268
|
+
"globals": [
|
|
269
|
+
{ "id": "inputItems", "direction": "in", "type": "array", "defaultValue": [], "triggerNodeId": "start" },
|
|
270
|
+
{ "id": "accumulator", "direction": "inout", "type": "number", "defaultValue": 0 },
|
|
271
|
+
{ "id": "result", "direction": "out", "type": "number" }
|
|
272
|
+
],
|
|
273
|
+
"nodes": [
|
|
274
|
+
{ "id": "loop1.currentItem", "type": "any", "binding": { "nodeId": "loop1", "outputId": "currentItem" } },
|
|
275
|
+
{ "id": "loop1.currentIteration", "type": "number", "binding": { "nodeId": "loop1", "outputId": "currentIteration" } },
|
|
276
|
+
{ "id": "loop1.collection", "type": "array", "binding": { "nodeId": "loop1", "outputId": "collection" } },
|
|
277
|
+
{ "id": "loop1.output", "type": "array", "binding": { "nodeId": "loop1", "outputId": "output" } },
|
|
278
|
+
{ "id": "bodyScript.output", "type": "any", "binding": { "nodeId": "bodyScript", "outputId": "output" } },
|
|
279
|
+
{ "id": "bodyScript.error", "type": "object", "binding": { "nodeId": "bodyScript", "outputId": "error" } }
|
|
280
|
+
],
|
|
281
|
+
"variableUpdates": {
|
|
282
|
+
"bodyScript": [
|
|
283
|
+
{
|
|
284
|
+
"variableId": "accumulator",
|
|
285
|
+
"expression": {
|
|
286
|
+
"type": "jsExpression",
|
|
287
|
+
"expression": "$vars.bodyScript.output",
|
|
288
|
+
"fieldType": "number"
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
Key points in this pattern:
|
|
298
|
+
- `bodyScript` has `"parentId": "loop1"` — places it inside the loop
|
|
299
|
+
- Script accesses `$vars.loop1.currentItem` for the current iteration value
|
|
300
|
+
- `variableUpdate` on `bodyScript` writes the script's return value back to `accumulator`, using the object `expression` form with `fieldType` matching `accumulator`'s declared `number` type
|
|
301
|
+
- `accumulator` is `inout` so it persists across iterations
|
|
302
|
+
- End node maps the final accumulated value to the `out` variable — also map `accumulator` itself, or `flow validate` warns `MISSING_OUTPUT_MAPPING` for the unmapped `inout` global
|
|
303
|
+
- Body edges use the loop's inner handles — `start` out, `continue` back — not `output`/`loopBack`
|
|
304
|
+
|
|
305
|
+
> **Prefer this pattern over post-processing `$vars.loop1.output`** when you need a running total. If you do consume the aggregate array instead, read each entry as `item.<bodyNodeId>.output.<field>` — see [Aggregated loop output](#aggregated-loop-output-varsloopidoutput).
|
|
306
|
+
|
|
307
|
+
## Complete Example — Multi-Node Loop Body (HTTP + Script)
|
|
308
|
+
|
|
309
|
+
Loop over items, fetch data per-iteration via HTTP, process with a script. Both body nodes have `parentId`.
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
{
|
|
313
|
+
"nodes": [
|
|
314
|
+
{
|
|
315
|
+
"id": "start",
|
|
316
|
+
"type": "core.trigger.manual",
|
|
317
|
+
"typeVersion": "1.0",
|
|
318
|
+
"display": { "label": "Manual trigger" },
|
|
319
|
+
"inputs": { "entryPointId": "..." }
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"id": "loop1",
|
|
323
|
+
"type": "core.logic.loop",
|
|
324
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
325
|
+
"display": { "label": "For each item" },
|
|
326
|
+
"inputs": {
|
|
327
|
+
"collection": "=js:[{ name: 'A', id: 1 }, { name: 'B', id: 2 }]",
|
|
328
|
+
"parallel": false
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"id": "fetchData1",
|
|
333
|
+
"type": "core.action.http.v2",
|
|
334
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
335
|
+
"display": { "label": "Fetch data" },
|
|
336
|
+
"inputs": { "branches": [], "timeout": "PT15M", "retryCount": 0, "detail": {} },
|
|
337
|
+
"parentId": "loop1"
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
"id": "processItem1",
|
|
341
|
+
"type": "core.action.script",
|
|
342
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
343
|
+
"display": { "label": "Process item" },
|
|
344
|
+
"inputs": {
|
|
345
|
+
"script": "return { name: $vars.loop1.currentItem.name, value: $vars.fetchData1.output.body.result };"
|
|
346
|
+
},
|
|
347
|
+
"parentId": "loop1"
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
"id": "end1",
|
|
351
|
+
"type": "core.control.end",
|
|
352
|
+
"typeVersion": "1.0",
|
|
353
|
+
"display": { "label": "End" },
|
|
354
|
+
"inputs": {},
|
|
355
|
+
"outputs": { "results": { "source": "=js:$vars.loop1.output" } }
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
"edges": [
|
|
359
|
+
{ "id": "e1", "sourceNodeId": "start", "sourcePort": "output", "targetNodeId": "loop1", "targetPort": "input" },
|
|
360
|
+
{ "id": "e2", "sourceNodeId": "loop1", "sourcePort": "start", "targetNodeId": "fetchData1", "targetPort": "input" },
|
|
361
|
+
{ "id": "e3", "sourceNodeId": "fetchData1", "sourcePort": "default", "targetNodeId": "processItem1", "targetPort": "input" },
|
|
362
|
+
{ "id": "e4", "sourceNodeId": "processItem1", "sourcePort": "success", "targetNodeId": "loop1", "targetPort": "continue" },
|
|
363
|
+
{ "id": "e5", "sourceNodeId": "loop1", "sourcePort": "success", "targetNodeId": "end1", "targetPort": "input" }
|
|
364
|
+
],
|
|
365
|
+
"variables": {
|
|
366
|
+
"globals": [
|
|
367
|
+
{ "id": "results", "direction": "out", "type": "array" }
|
|
368
|
+
],
|
|
369
|
+
"nodes": [
|
|
370
|
+
{ "id": "loop1.currentItem", "type": "any", "binding": { "nodeId": "loop1", "outputId": "currentItem" } },
|
|
371
|
+
{ "id": "loop1.currentIteration", "type": "number", "binding": { "nodeId": "loop1", "outputId": "currentIteration" } },
|
|
372
|
+
{ "id": "loop1.collection", "type": "array", "binding": { "nodeId": "loop1", "outputId": "collection" } },
|
|
373
|
+
{ "id": "loop1.output", "type": "array", "binding": { "nodeId": "loop1", "outputId": "output" } },
|
|
374
|
+
{ "id": "fetchData1.output", "type": "object", "binding": { "nodeId": "fetchData1", "outputId": "output" } },
|
|
375
|
+
{ "id": "fetchData1.error", "type": "object", "binding": { "nodeId": "fetchData1", "outputId": "error" } },
|
|
376
|
+
{ "id": "processItem1.output", "type": "any", "binding": { "nodeId": "processItem1", "outputId": "output" } },
|
|
377
|
+
{ "id": "processItem1.error", "type": "object", "binding": { "nodeId": "processItem1", "outputId": "error" } }
|
|
378
|
+
]
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
- Both `fetchData1` and `processItem1` have `"parentId": "loop1"` — omitting it on either causes null outputs at runtime while validation passes
|
|
384
|
+
- HTTP body uses `start`/`default` ports; script returns via `success`/`continue`
|
|
385
|
+
- `detail` on HTTP node is abbreviated — populate via `uip maestro flow node configure`
|
|
386
|
+
- `results` receives the aggregate array verbatim, so each entry looks like `{ "fetchData1": { "output": … }, "processItem1": { "output": … } }` — both body nodes as sibling keys. Flatten it in a Script node after the loop if the consumer expects bare values. See [Aggregated loop output](#aggregated-loop-output-varsloopidoutput).
|
|
387
|
+
|
|
388
|
+
## Debug
|
|
389
|
+
|
|
390
|
+
| Error | Cause | Fix |
|
|
391
|
+
| --- | --- | --- |
|
|
392
|
+
| Collection is empty or null | Expression evaluates to null/undefined | Check `collection` expression and upstream output |
|
|
393
|
+
| `$vars.loop1.currentItem` is undefined | Missing node variable binding or missing `parentId` | Add `loop1.currentItem` to `variables.nodes` and set `parentId` on body nodes |
|
|
394
|
+
| `$vars.loop1.currentIndex` is undefined | The output is named `currentIteration` (1-based); there is no `currentIndex`, and a binding to it still validates | Use `$vars.<loopId>.currentIteration` and subtract 1 if you need a 0-based index |
|
|
395
|
+
| `flow validate` fails: `[MIGRATION] … 1.9→1.10 … Offending field(s): variables.variableUpdates.<nodeId>.0.expression` | `variableUpdates[].expression` written as a legacy `=js:` string | Rewrite with `Edit` as `{ "type": "jsExpression", "expression": "<bare JS>", "fieldType": "<target variable type>" }` |
|
|
396
|
+
| `flow validate` fails: `[variables.variableUpdates.<nodeId>[0].expression] Invalid input` | The expression object is malformed — missing one of the three keys, carrying an extra key, or an unknown `type` | Use exactly `type` + `expression` + `fieldType`; the object is strict |
|
|
397
|
+
| `flow validate` fails: `Edge references undeclared source handle "output"` / `target handle "loopBack"` | Loop body wired to ports that do not exist | Use the inner handles: `start` out of the loop, `continue` back into it |
|
|
398
|
+
| State variable not updating across iterations | Body node missing `parentId` | Add `"parentId": "<loopId>"` to every node inside the loop body |
|
|
399
|
+
| State variable becomes `NaN` | variableUpdate expression uses `$vars.<loopId>.currentItem` | Loop variables are not available in variableUpdate expressions. Do the computation in the script and reference `$vars.<bodyNodeId>.output` in the variableUpdate |
|
|
400
|
+
| Downstream value is `NaN`/`undefined` after reducing `$vars.<loopId>.output` | Entries are keyed by body node id, not the body's bare return value | Read `item.<bodyNodeId>.output.<field>`, not `item.<field>` — see [Aggregated loop output](#aggregated-loop-output-varsloopidoutput) |
|
|
401
|
+
| Infinite loop | Edges wired incorrectly | Ensure only the body's `continue` edge creates the cycle, not arbitrary edges |
|
|
402
|
+
| No output after loop | Missing `success` edge | Wire the `success` port to the next downstream node |
|
|
403
|
+
| Per-item step runs once, after the last iteration | Step wired off the outer `success` port instead of inside the body | Set `"parentId": "<loopId>"` on the step and splice it into the body just before the edge into `continue` (empty body: `start` → step → `continue`). Wire `success` to the step's former downstream node |
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Loop Node — Planning
|
|
2
|
+
|
|
3
|
+
## Node Type
|
|
4
|
+
|
|
5
|
+
`core.logic.loop`
|
|
6
|
+
|
|
7
|
+
## When to Use
|
|
8
|
+
|
|
9
|
+
Use a Loop node to iterate over a collection of items. Supports sequential and parallel execution.
|
|
10
|
+
|
|
11
|
+
### Selection Heuristics
|
|
12
|
+
|
|
13
|
+
| Situation | Use Loop? |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Process each item in an array | Yes |
|
|
16
|
+
| Run the same operation on multiple inputs concurrently | Yes (with `parallel: true`) |
|
|
17
|
+
| Simple data transformation on a collection | No — use [Transform](../transform/planning.md) |
|
|
18
|
+
| Distribute work items to robots | No — use [Queue](../queue/planning.md) |
|
|
19
|
+
|
|
20
|
+
## Ports
|
|
21
|
+
|
|
22
|
+
The loop is a container node, so its handles split across an outer and an inner boundary.
|
|
23
|
+
|
|
24
|
+
| Boundary | Input Port(s) | Output Port(s) |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| outer | `input` | `success`, `error` |
|
|
27
|
+
| inner | `continue`, `break` | `start` |
|
|
28
|
+
|
|
29
|
+
- `start` — feeds the first node inside the loop body
|
|
30
|
+
- `continue` — receives the edge returning from the last node inside the loop body
|
|
31
|
+
- `break` — early exit from inside the body (only when `breakEnabled`)
|
|
32
|
+
- `success` — fires after all iterations complete
|
|
33
|
+
- `error` — implicit error port shared with all action nodes; fires when the loop or an iteration throws. See [Implicit error port on action nodes](../../../shared/file-format.md#implicit-error-port-on-action-nodes).
|
|
34
|
+
|
|
35
|
+
> Aggregated results are an output **variable** (`$vars.<loopId>.output`), not a port.
|
|
36
|
+
|
|
37
|
+
## Key Inputs
|
|
38
|
+
|
|
39
|
+
| Input | Required | Description |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| `collection` | Yes | Expression pointing to an array (e.g., `$vars.fetchData.output.body.items`) |
|
|
42
|
+
| `parallel` | No | `true` to execute all iterations concurrently (default: sequential) |
|
|
43
|
+
|
|
44
|
+
## Loop Variables (available inside loop body only)
|
|
45
|
+
|
|
46
|
+
- `$vars.<loopId>.currentItem` — the item being processed in this iteration
|
|
47
|
+
- `$vars.<loopId>.currentIteration` — 1-based iteration number (there is no `currentIndex`)
|
|
48
|
+
- `$vars.<loopId>.collection` — the full collection
|
|
49
|
+
|
|
50
|
+
Where `<loopId>` is the loop node's `id` (e.g., `$vars.loop1.currentItem`).
|
|
51
|
+
|
|
52
|
+
After the loop, `$vars.<loopId>.output` holds one entry per iteration, each keyed by body node id — plan a flattening step if a downstream consumer needs bare values.
|
|
53
|
+
|
|
54
|
+
## Wiring Rules
|
|
55
|
+
|
|
56
|
+
- The loop body starts from the loop node's inner `start` port
|
|
57
|
+
- The last node in the loop body connects back to the loop's inner `continue` port
|
|
58
|
+
- After all iterations, execution continues once from the outer `success` port. Per-item steps go in the body, never after `success`
|
|
59
|
+
- Do not create cycles except through the `continue` handle
|
|
60
|
+
- **Every node inside the loop body must have `"parentId": "<loopId>"`** — without this, variableUpdates will not fire per-iteration and loop variables will be inaccessible
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Merge Node — Implementation
|
|
2
|
+
|
|
3
|
+
## Node Type
|
|
4
|
+
|
|
5
|
+
`core.logic.merge`
|
|
6
|
+
|
|
7
|
+
## Registry Validation
|
|
8
|
+
|
|
9
|
+
Run:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uip maestro flow registry get core.logic.merge --output json
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Confirm that input port `input` accepts multiple connections and output port `output` exists. Set the node instance `typeVersion` to the response's `version` field; do not hardcode it.
|
|
16
|
+
|
|
17
|
+
## JSON Structure
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"id": "joinBranches",
|
|
22
|
+
"type": "core.logic.merge",
|
|
23
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
24
|
+
"display": { "label": "Join Branches" },
|
|
25
|
+
"inputs": {}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Adding, Editing, and Wiring
|
|
30
|
+
|
|
31
|
+
For add, delete, and wiring procedures, see [editing-operations.md](../../editing-operations.md). Use the JSON structure above for node-specific `inputs`.
|
|
32
|
+
|
|
33
|
+
- `input` accepts multiple incoming edges, one per parallel branch. All branches must reach the merge before execution continues.
|
|
34
|
+
- `output` has one outgoing edge to the next downstream node.
|
|
35
|
+
|
|
36
|
+
See [editing-operations.md](../../editing-operations.md) for edge-add procedures.
|
|
37
|
+
|
|
38
|
+
## Debug
|
|
39
|
+
|
|
40
|
+
| Error | Cause | Fix |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| Merge never completes | One parallel branch has no path to the merge node | Ensure all forked branches reach the merge |
|
|
43
|
+
| Unexpected execution order | Branches are assumed to complete in order | Merge waits for all branches; do not depend on arrival order |
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Merge Node — Planning
|
|
2
|
+
|
|
3
|
+
## Node Type
|
|
4
|
+
|
|
5
|
+
`core.logic.merge`
|
|
6
|
+
|
|
7
|
+
## Use
|
|
8
|
+
|
|
9
|
+
Use a Merge node to synchronize two or more parallel branches before continuing. It waits for all incoming paths to complete.
|
|
10
|
+
|
|
11
|
+
- Use it when parallel branches must rejoin or after one node forks into multiple downstream branches.
|
|
12
|
+
- Do not use it for sequential pipelines; wire nodes directly.
|
|
13
|
+
- Do not use it to join the `true`/`false` branches of one Decision (or the cases of one Switch); wire both ports straight into the next node, which accepts several incoming edges. A Merge waits for ALL inputs and a Decision emits exactly one, so the run would hang forever; `flow validate` refuses this shape.
|
|
14
|
+
|
|
15
|
+
## Ports
|
|
16
|
+
|
|
17
|
+
| Input Port | Output Port(s) |
|
|
18
|
+
| --- | --- |
|
|
19
|
+
| `input` (accepts multiple connections) | `output` |
|
|
20
|
+
|
|
21
|
+
## Wiring Rules
|
|
22
|
+
|
|
23
|
+
- Connect each parallel branch's terminal node to the Merge node's `input` port; multiple incoming edges may use the same port.
|
|
24
|
+
- Execution continues from `output` only after all incoming paths complete.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# Queue Node — Implementation
|
|
2
|
+
|
|
3
|
+
## Node Types
|
|
4
|
+
|
|
5
|
+
| Node Type | Description |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `core.action.queue.create` | Fire-and-forget queue item creation |
|
|
8
|
+
| `core.action.queue.create-and-wait` | Create queue item and wait for result |
|
|
9
|
+
|
|
10
|
+
## Registry Validation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
uip maestro flow registry get core.action.queue.create --output json
|
|
14
|
+
uip maestro flow registry get core.action.queue.create-and-wait --output json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Confirm: input port `input`, output port `success`. Set each node instance's `typeVersion` to the `version` field from the matching response — do not hardcode it.
|
|
18
|
+
|
|
19
|
+
## JSON Structure
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"id": "enqueueItem",
|
|
24
|
+
"type": "core.action.queue.create",
|
|
25
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
26
|
+
"display": { "label": "Enqueue Invoice" },
|
|
27
|
+
"inputs": {
|
|
28
|
+
"queue": "InvoiceProcessingQueue",
|
|
29
|
+
"itemData": "=js:JSON.stringify({ orderId: $vars.order.id, amount: $vars.order.total })",
|
|
30
|
+
"priority": "High",
|
|
31
|
+
"reference": "=js:$vars.order.id",
|
|
32
|
+
"deferDate": "2026-04-01T10:00:00Z",
|
|
33
|
+
"dueDate": "2026-04-07T17:00:00Z"
|
|
34
|
+
},
|
|
35
|
+
"outputs": {
|
|
36
|
+
"error": {
|
|
37
|
+
"type": "object",
|
|
38
|
+
"description": "Error information if the queue operation fails",
|
|
39
|
+
"source": "=Error",
|
|
40
|
+
"var": "error"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Adding / Editing
|
|
47
|
+
|
|
48
|
+
For step-by-step add, delete, and wiring procedures, see [editing-operations.md](../../editing-operations.md). Use the JSON structure above for the node-specific `inputs`.
|
|
49
|
+
|
|
50
|
+
## Wait Variant
|
|
51
|
+
|
|
52
|
+
`core.action.queue.create-and-wait` blocks execution until the queue item is processed by a robot. The processed result is available via `$vars.{nodeId}.output`.
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
{
|
|
56
|
+
"id": "processAndWait",
|
|
57
|
+
"type": "core.action.queue.create-and-wait",
|
|
58
|
+
"typeVersion": "<DEFINITION_VERSION>",
|
|
59
|
+
"display": { "label": "Process and Wait" },
|
|
60
|
+
"inputs": {
|
|
61
|
+
"queue": "InvoiceProcessingQueue",
|
|
62
|
+
"itemData": "=js:JSON.stringify({ invoiceId: $vars.invoiceId })"
|
|
63
|
+
},
|
|
64
|
+
"outputs": {
|
|
65
|
+
"error": {
|
|
66
|
+
"type": "object",
|
|
67
|
+
"description": "Error information if the queue operation fails",
|
|
68
|
+
"source": "=Error",
|
|
69
|
+
"var": "error"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Debug
|
|
76
|
+
|
|
77
|
+
| Error | Cause | Fix |
|
|
78
|
+
| --- | --- | --- |
|
|
79
|
+
| Queue not found | Queue name doesn't match Orchestrator | Verify queue name in Orchestrator |
|
|
80
|
+
| `itemData` invalid | Not valid JSON | Ensure `JSON.stringify()` wraps the data object |
|
|
81
|
+
| Queue item stuck | No robot available to process | Check Orchestrator robot allocation |
|
|
82
|
+
| Wait timeout | Robot took too long to process item | Check queue processing time and robot availability |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Queue Node — Planning
|
|
2
|
+
|
|
3
|
+
## Node Types
|
|
4
|
+
|
|
5
|
+
| Node Type | Description |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| `core.action.queue.create` | Create a queue item and continue immediately (fire-and-forget) |
|
|
8
|
+
| `core.action.queue.create-and-wait` | Create a queue item and wait for processing to complete |
|
|
9
|
+
|
|
10
|
+
## When to Use
|
|
11
|
+
|
|
12
|
+
Use Queue nodes to distribute work items to robots via Orchestrator queues.
|
|
13
|
+
|
|
14
|
+
### Selection Heuristics
|
|
15
|
+
|
|
16
|
+
| Situation | Use Queue? |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Distribute work to robots — fire-and-forget | Yes (`create`) |
|
|
19
|
+
| Distribute work and need the result before continuing | Yes (`create-and-wait`) |
|
|
20
|
+
| Direct process invocation with known inputs | No — use [RPA Workflow](../rpa/planning.md) |
|
|
21
|
+
| Iterate over items sequentially in the flow | No — use [Loop](../loop/planning.md) |
|
|
22
|
+
|
|
23
|
+
## Ports
|
|
24
|
+
|
|
25
|
+
| Input Port | Output Port(s) |
|
|
26
|
+
| --- | --- |
|
|
27
|
+
| `input` | `success` |
|
|
28
|
+
|
|
29
|
+
## Key Inputs
|
|
30
|
+
|
|
31
|
+
| Input | Required | Description |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| `queue` | Yes | Orchestrator queue name |
|
|
34
|
+
| `itemData` | No | JSON payload for the queue item |
|
|
35
|
+
| `priority` | No | `Low`, `Normal` (default), `High` |
|
|
36
|
+
| `reference` | No | Tracking reference string |
|
|
37
|
+
| `deferDate` | No | ISO 8601 — earliest time to process |
|
|
38
|
+
| `dueDate` | No | ISO 8601 — deadline for processing |
|
|
39
|
+
|
|
40
|
+
## Common Pattern — Fan-Out to Queue
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
Manual Trigger -> Script (split batch) -> Loop -> Queue Create (per item) -> End Loop -> End
|
|
44
|
+
```
|