@unstable-dev/unmeshed-mcp 0.1.0

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.
Files changed (47) hide show
  1. package/README.md +64 -0
  2. package/dist/auth.d.ts +6 -0
  3. package/dist/auth.js +11 -0
  4. package/dist/client.d.ts +46 -0
  5. package/dist/client.js +97 -0
  6. package/dist/config.d.ts +10 -0
  7. package/dist/config.js +31 -0
  8. package/dist/get-docs.d.ts +8 -0
  9. package/dist/get-docs.js +64 -0
  10. package/dist/index.d.ts +2 -0
  11. package/dist/index.js +35 -0
  12. package/dist/server.d.ts +4 -0
  13. package/dist/server.js +203 -0
  14. package/knowledge/README.md +16 -0
  15. package/knowledge/SKILL.md +359 -0
  16. package/knowledge/assets/patterns.md +637 -0
  17. package/knowledge/execution/debugging-guide.md +18 -0
  18. package/knowledge/execution/process-run.schema.md +24 -0
  19. package/knowledge/execution/step-run.schema.md +21 -0
  20. package/knowledge/process-definition.schema.md +36 -0
  21. package/knowledge/references/integrations.md +914 -0
  22. package/knowledge/references/steps-knowledge.md +834 -0
  23. package/knowledge/step-definition.schema.md +140 -0
  24. package/knowledge/step-output-paths.md +45 -0
  25. package/knowledge/steps/DECISION_ENGINE.md +248 -0
  26. package/knowledge/steps/DEPENDSON.md +296 -0
  27. package/knowledge/steps/EXIT.md +220 -0
  28. package/knowledge/steps/FAIL.md +198 -0
  29. package/knowledge/steps/FLOW_GATEWAY.md +405 -0
  30. package/knowledge/steps/FOREACH.md +250 -0
  31. package/knowledge/steps/HTTP.md +183 -0
  32. package/knowledge/steps/JAVASCRIPT.md +192 -0
  33. package/knowledge/steps/JQ.md +189 -0
  34. package/knowledge/steps/LIST.md +279 -0
  35. package/knowledge/steps/NOOP.md +165 -0
  36. package/knowledge/steps/PARALLEL.md +366 -0
  37. package/knowledge/steps/PYTHON.md +206 -0
  38. package/knowledge/steps/SEND_RESPONSE.md +301 -0
  39. package/knowledge/steps/SQLITE.md +301 -0
  40. package/knowledge/steps/SUB_PROCESS.md +296 -0
  41. package/knowledge/steps/SWITCH.md +369 -0
  42. package/knowledge/steps/UPDATE_STEP.md +257 -0
  43. package/knowledge/steps/WAIT.md +218 -0
  44. package/knowledge/steps/WHILE.md +328 -0
  45. package/knowledge/steps/WORKER.md +233 -0
  46. package/knowledge/system-prompt.md +274 -0
  47. package/package.json +39 -0
@@ -0,0 +1,637 @@
1
+ # Unmeshed Workflow Patterns & Templates
2
+
3
+ Use these as starting scaffolds. Replace all `<PLACEHOLDER>` values.
4
+
5
+ ---
6
+
7
+ ## Pattern A — Linear Data Pipeline
8
+
9
+ **Use case:** Receive API input → transform → call integration → format output → store
10
+
11
+ ```json
12
+ {
13
+ "orgId": 1,
14
+ "namespace": "default",
15
+ "name": "<workflow-name>",
16
+ "version": 1,
17
+ "type": "API_ORCHESTRATION",
18
+ "description": "<description>",
19
+ "configuration": null,
20
+ "steps": [
21
+ {
22
+ "orgId": 1, "namespace": "default",
23
+ "name": "map_input_fields", "type": "JAVASCRIPT", "ref": "map_input_fields",
24
+ "optional": false, "createdBy": "system", "updatedBy": "system",
25
+ "description": "Map and validate incoming API input", "label": null,
26
+ "created": 1700000000000, "updated": 1700000000000,
27
+ "configuration": { "errorPolicyName": null, "useCache": false, "cacheKey": null, "cacheTimeoutSeconds": 0, "stream": false, "streamAllStatuses": false, "preExecutionScript": null, "constructInputFromScript": false, "scriptLanguage": null, "jqTransformer": null, "rateLimitMaxRequests": 0, "rateLimitWindowSeconds": 0 },
28
+ "children": [],
29
+ "input": {
30
+ "script": "(steps, context) => {\n const input = context.input || {};\n return {\n field1: input.field1 || '',\n field2: input.field2 || ''\n };\n}"
31
+ },
32
+ "output": null
33
+ },
34
+ {
35
+ "orgId": 1, "namespace": "default",
36
+ "name": "<integration_step>", "type": "INTEGRATION", "ref": "<integration_ref>_1",
37
+ "optional": false, "createdBy": "system", "updatedBy": "system",
38
+ "description": null, "label": null,
39
+ "created": 1700000000000, "updated": 1700000000000,
40
+ "configuration": { "errorPolicyName": null, "useCache": false, "cacheKey": null, "cacheTimeoutSeconds": 0, "stream": false, "streamAllStatuses": false, "preExecutionScript": null, "constructInputFromScript": false, "scriptLanguage": null, "jqTransformer": null, "rateLimitMaxRequests": 0, "rateLimitWindowSeconds": 0 },
41
+ "children": [],
42
+ "input": { /* see integrations.md */ },
43
+ "output": null
44
+ }
45
+ ],
46
+ "defaultInput": null, "defaultOutput": null, "outputMapping": null,
47
+ "signature": null, "metadata": null, "tags": null, "dependencies": null, "dependents": null
48
+ }
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Pattern B — LLM Classify → Decision Route
54
+
55
+ **Use case:** Input → map fields → LLM classifies → DECISION_ENGINE routes
56
+
57
+ Steps: `JAVASCRIPT (map)` → `INTEGRATION (llm-claude)` → `DECISION_ENGINE`
58
+
59
+ Key wiring:
60
+ - LLM output accessed as: `steps.llm_claude_1.output.results.<field>`
61
+ - Decision context maps LLM output fields to decision table column names
62
+ - Always defensively read LLM output: `(steps.llm_claude_1.output.results || steps.llm_claude_1.output.result || {})`
63
+
64
+ JAVASCRIPT (map) input:
65
+ ```javascript
66
+ (steps, context) => {
67
+ const input = context.input || {};
68
+ return {
69
+ text: input.text || '',
70
+ userId: input.userId || ''
71
+ };
72
+ }
73
+ ```
74
+
75
+ INTEGRATION (llm-claude) input:
76
+ ```json
77
+ {
78
+ "type": "llm-claude",
79
+ "name": "unmeshed",
80
+ "publishProperties": { "allowedTokens": 300, "temperature": 0.0 },
81
+ "messageBody": {
82
+ "systemPrompt": "Classify the input. Return only valid JSON with fields: category, urgency.",
83
+ "userPrompt": "Text: {{steps.map_input_fields.output.result.text}}\n\nReturn: {\"category\": \"...\", \"urgency\": \"low|medium|high\"}"
84
+ }
85
+ }
86
+ ```
87
+
88
+ DECISION_ENGINE input:
89
+ ```json
90
+ {
91
+ "decisionRuleStrategy": "FIRST_MATCH",
92
+ "decisionTable": "<your-table-name>",
93
+ "decisionContext": {
94
+ "Category": "{{steps.llm_claude_1.output.results.category}}",
95
+ "Urgency": "{{steps.llm_claude_1.output.results.urgency}}"
96
+ },
97
+ "decisionTableVersion": "",
98
+ "decisionOutputColumns": ["AssignTo", "Priority"]
99
+ }
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Pattern C — State-Gated Branch (Create or Reuse Resource)
105
+
106
+ **Use case:** Check if resource exists in state → if yes use it, if no create it
107
+
108
+ Steps:
109
+ 1. `PERSISTED_STATE` (READ) — check if resource ID is stored
110
+ 2. `SWITCH` — branch on `steps.persisted_state_1.output.result` being null or not
111
+ - Branch `new-resource` (LIST): create resource → store ID in PERSISTED_STATE (UPSERT)
112
+ - Branch `existing-resource` (LIST or NOOP): use stored ID directly
113
+
114
+ SWITCH script pattern:
115
+ ```javascript
116
+ (steps, context) => {
117
+ if (steps.persisted_state_1.output.result) {
118
+ return 'existing-resource';
119
+ }
120
+ return 'new-resource';
121
+ }
122
+ ```
123
+
124
+ SWITCH responseMapping:
125
+ ```json
126
+ [
127
+ { "targetRef": "existing-resource", "value": "existing-resource" },
128
+ { "targetRef": "new-resource", "value": "new-resource" },
129
+ { "targetRef": "new-resource", "defaultBranch": true }
130
+ ]
131
+ ```
132
+
133
+ PERSISTED_STATE (READ) input:
134
+ ```json
135
+ { "operation": "READ", "name": "my-spreadsheet-id", "path": "$" }
136
+ ```
137
+
138
+ PERSISTED_STATE (UPSERT, inside LIST branch) input:
139
+ ```json
140
+ {
141
+ "operation": "UPSERT",
142
+ "name": "my-spreadsheet-id",
143
+ "path": "$",
144
+ "value": "{{steps.google_sheets_1.output.spreadsheetId}}"
145
+ }
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Pattern D — Parallel Sub-Process Fan-out
151
+
152
+ **Use case:** Gate check → run multiple workflows concurrently
153
+
154
+ Steps:
155
+ 1. `PERSISTED_STATE` (READ) — read a flag
156
+ 2. `SWITCH` — if flag true: proceed, else EXIT
157
+ - Branch NOOP → continues to next top-level step
158
+ - Branch EXIT → terminates
159
+ 3. `PARALLEL` — children are all SUB_PROCESS steps
160
+
161
+ PARALLEL input:
162
+ ```json
163
+ { "failIfAnyBranchFails": true }
164
+ ```
165
+
166
+ PARALLEL children (SUB_PROCESS steps):
167
+ ```json
168
+ [
169
+ {
170
+ "orgId": 1, "namespace": "default",
171
+ "name": "process-a", "type": "SUB_PROCESS", "ref": "process-a",
172
+ "optional": false, "createdBy": "system", "updatedBy": "system",
173
+ "description": null, "label": null,
174
+ "created": 1700000000000, "updated": 1700000000000,
175
+ "configuration": { "errorPolicyName": null, "useCache": false, "cacheKey": null, "cacheTimeoutSeconds": 0, "stream": false, "streamAllStatuses": false, "preExecutionScript": null, "constructInputFromScript": false, "scriptLanguage": null, "jqTransformer": null, "rateLimitMaxRequests": 0, "rateLimitWindowSeconds": 0 },
176
+ "children": [],
177
+ "input": { "processName": "process-a", "waitForCompletion": true },
178
+ "output": null
179
+ }
180
+ ]
181
+ ```
182
+
183
+ ---
184
+
185
+ ## Pattern E — Classify → Store All → Conditionally Notify
186
+
187
+ **Use case:** Every record is stored (e.g. Google Sheets), but notifications (e.g. Slack) only fire for specific conditions (e.g. critical urgency).
188
+
189
+ Steps:
190
+ 1. `JAVASCRIPT` — map/validate input fields
191
+ 2. `INTEGRATION` (llm-claude) — classify/analyse with LLM
192
+ 3. `DECISION_ENGINE` — route based on classification rules
193
+ 4. `JAVASCRIPT` — format output record + pre-build notification message string
194
+ 5. `INTEGRATION` (google-sheets APPEND_ROWS) — always write to sheet unconditionally
195
+ 6. `SWITCH` — check condition (e.g. `topic === 'technical_issue' && urgency === 'critical'`)
196
+ - Branch `notify` → `INTEGRATION` (slack-messaging) directly (no LIST wrapper needed)
197
+ - Branch `skip` → `NOOP` (do nothing)
198
+
199
+ **Key insight:** The SWITCH comes **after** the unconditional storage step. This ensures every record is persisted regardless of whether a notification fires.
200
+
201
+ SWITCH branch direct to INTEGRATION example:
202
+ ```json
203
+ {
204
+ "type": "SWITCH",
205
+ "children": [
206
+ {
207
+ "orgId": 1, "namespace": "default",
208
+ "name": "slack_messaging", "type": "INTEGRATION", "ref": "slack_messaging_1",
209
+ "optional": false, "createdBy": "system", "updatedBy": "system",
210
+ "description": null, "label": null,
211
+ "created": 1700000000000, "updated": 1700000000000,
212
+ "configuration": { "errorPolicyName": null, "useCache": false, "cacheKey": null, "cacheTimeoutSeconds": 0, "stream": false, "streamAllStatuses": false, "preExecutionScript": null, "constructInputFromScript": false, "scriptLanguage": null, "jqTransformer": null, "rateLimitMaxRequests": 0, "rateLimitWindowSeconds": 0 },
213
+ "children": [],
214
+ "input": {
215
+ "type": "slack-messaging",
216
+ "name": "",
217
+ "messageBody": {
218
+ "type": "MESSAGE",
219
+ "message": "{{steps.format_step.output.result.slackMessage}}",
220
+ "messageBlocks": [{ "type": "section", "text": { "type": "mrkdwn", "text": "{{steps.format_step.output.result.slackMessage}}" } }]
221
+ },
222
+ "publishProperties": { "durationType": "MINUTES", "receiverType": "CHANNEL" }
223
+ },
224
+ "output": null
225
+ },
226
+ {
227
+ "orgId": 1, "namespace": "default",
228
+ "name": "no_notification", "type": "NOOP", "ref": "no-notification",
229
+ "optional": false, "createdBy": "system", "updatedBy": "system",
230
+ "description": null, "label": null,
231
+ "created": 1700000000000, "updated": 1700000000000,
232
+ "configuration": { "errorPolicyName": null, "useCache": false, "cacheKey": null, "cacheTimeoutSeconds": 0, "stream": false, "streamAllStatuses": false, "preExecutionScript": null, "constructInputFromScript": false, "scriptLanguage": null, "jqTransformer": null, "rateLimitMaxRequests": 0, "rateLimitWindowSeconds": 0 },
233
+ "children": [],
234
+ "input": {},
235
+ "output": null
236
+ }
237
+ ],
238
+ "input": {
239
+ "script": "(steps, context) => {\n const topic = String(steps.format_step.output.result.topic || '').toLowerCase();\n const urgency = String(steps.format_step.output.result.urgency || '').toLowerCase();\n if (topic === 'technical_issue' && urgency === 'critical') return 'notify';\n return 'skip';\n}",
240
+ "responseMapping": [
241
+ { "targetRef": "slack_messaging_1", "value": "notify" },
242
+ { "targetRef": "no-notification", "value": "skip" },
243
+ { "targetRef": "no-notification", "defaultBranch": true }
244
+ ]
245
+ }
246
+ }
247
+ ```
248
+
249
+ ---
250
+
251
+ ## Pattern F — HTTP API Call → Normalize → Store
252
+
253
+ **Use case:** Call external REST API, normalize the response, store to DB or sheet.
254
+
255
+ Steps:
256
+ 1. `JAVASCRIPT` — build request payload from context input
257
+ 2. `HTTP` — call the external API
258
+ 3. `JAVASCRIPT` — normalize/extract fields from API response
259
+ 4. `INTEGRATION` (postgres or google-sheets) — store the data
260
+
261
+ Key wiring for HTTP step:
262
+ ```json
263
+ {
264
+ "type": "HTTP",
265
+ "ref": "http_1",
266
+ "input": {
267
+ "method": "POST",
268
+ "url": "https://api.example.com/data",
269
+ "headers": { "Content-Type": "application/json", "Authorization": "Bearer {{secrets.api_token}}" },
270
+ "params": {},
271
+ "body": {
272
+ "type": "json",
273
+ "content": {
274
+ "query": "{{steps.map_input_fields.output.result.query}}"
275
+ }
276
+ },
277
+ "repeatUntilEnabled": null,
278
+ "repeatUntilCondition": { "script": "(steps, context) => { return false; }" },
279
+ "repeatIntervalSeconds": null,
280
+ "maxRepeatCount": null,
281
+ "includeFullResponseString": false,
282
+ "noEncode": false,
283
+ "extraLongTimeouts": false
284
+ }
285
+ }
286
+ ```
287
+
288
+ Normalize JS after HTTP:
289
+ ```javascript
290
+ (steps, context) => {
291
+ const response = steps.http_1.output.response || {};
292
+ return {
293
+ id: response.id || '',
294
+ name: response.name || '',
295
+ value: response.data?.value || 0
296
+ };
297
+ }
298
+ ```
299
+
300
+ ---
301
+
302
+ ## Pattern G — Error-Aware Exit Branches
303
+
304
+ **Use case:** Validate input → exit with FAILED if invalid, else continue processing.
305
+
306
+ Steps:
307
+ 1. `JAVASCRIPT` — validate input fields, return `{ valid: true/false, reason: '...' }`
308
+ 2. `SWITCH` — branch on `valid`
309
+ - Branch `invalid` → `EXIT` (status: FAILED)
310
+ - Branch `valid` → `LIST` (continue with main logic)
311
+
312
+ SWITCH script:
313
+ ```javascript
314
+ (steps, context) => {
315
+ if (!steps.validate_input.output.result.valid) return 'invalid';
316
+ return 'valid';
317
+ }
318
+ ```
319
+
320
+ EXIT step inside invalid branch:
321
+ ```json
322
+ {
323
+ "type": "EXIT",
324
+ "input": {
325
+ "message": "{{steps.validate_input.output.result.reason}}",
326
+ "status": "FAILED"
327
+ }
328
+ }
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Ref Naming Conventions
334
+
335
+ | Step type | Ref pattern | Example |
336
+ |---|---|---|
337
+ | INTEGRATION | `<name>_<n>` | `google_sheets_1`, `llm_claude_1`, `http_1` |
338
+ | JAVASCRIPT | same as `name` | `map_input_fields`, `format_output` |
339
+ | PYTHON | same as `name` | `extract_content` |
340
+ | NOOP | same as `name` | `job_config`, `settings` |
341
+ | SWITCH | `switch_<n>` | `switch_1` |
342
+ | PARALLEL | `parallel_<n>` | `parallel_1` |
343
+ | PERSISTED_STATE | `persisted_state_<n>` | `persisted_state_1` |
344
+ | LIST (branch) | descriptive kebab | `new-sheet`, `existing-sheet`, `invalid-input` |
345
+ | EXIT | `exit_<n>` | `exit_1` |
346
+ | SUB_PROCESS | same as `name` | `send-welcome-email` |
347
+
348
+ ---
349
+
350
+ ## Data Flow Cheatsheet
351
+
352
+ ```
353
+ context.input.<field> ← workflow API input
354
+ steps.<ref>.output.response.<field> ← native HTTP response body
355
+ steps.<ref>.output.statusCode ← native HTTP status code
356
+ steps.<ref>.output.result.<field> ← JAVASCRIPT / PYTHON / most INTEGRATION output
357
+ steps.<ref>.output.results.<field> ← LLM Claude output (note: results plural)
358
+ steps.<ref>.output.<field> ← NOOP output (no .result wrapper)
359
+ steps.<ref>.output.spreadsheetId ← Google Sheets CREATE output
360
+ steps.<ref>.output.result.rows ← PostgreSQL / MySQL SELECT output
361
+ steps.<ref>.output.id ← Google Drive upload output (file ID)
362
+ steps.<ref>.output.webViewLink ← Google Drive upload output (share URL)
363
+ steps.<ref>.output.status ← HTTP integration response status code
364
+ steps.__self.id ← current step ID (inside scripts)
365
+ context.id ← workflow process/run ID
366
+ ```
367
+
368
+ ---
369
+
370
+ ## Pattern H — WAIT for Async Side Effect
371
+
372
+ **Use case:** Trigger an async action (e.g. send email via sub-process), wait for it to arrive/propagate, then assert or read results.
373
+
374
+ Steps:
375
+ 1. `SUB_PROCESS` — trigger the action (e.g. send email)
376
+ 2. `WAIT` — pause for delivery/propagation time
377
+ 3. `INTEGRATION` — read/verify the result (e.g. readEmail)
378
+ 4. `JAVASCRIPT` — assert on the result
379
+
380
+ WAIT input:
381
+ ```javascript
382
+ (steps, context) => {
383
+ return {
384
+ "waitUntil": steps.__self.startTime + (25 * 1000) // 25 seconds
385
+ };
386
+ }
387
+ ```
388
+
389
+ **Key rule:** Always use `steps.__self.startTime` (not `Date.now()`) as the base — it's stable within a WAIT instance.
390
+
391
+ ---
392
+
393
+ ## Pattern I — FOREACH Loop
394
+
395
+ **Use case:** Iterate over an array and run the same steps for each element.
396
+
397
+ Structure:
398
+ ```
399
+ FOREACH (inputArray, concurrency)
400
+ └── LIST
401
+ └── [step(s) for each iteration]
402
+ JAVASCRIPT (collect results)
403
+ JAVASCRIPT (assert on collected results)
404
+ ```
405
+
406
+ FOREACH input:
407
+ ```json
408
+ {
409
+ "inputArray": [1, 2, 3, 4, 5],
410
+ "concurrency": 1
411
+ }
412
+ ```
413
+
414
+ Collecting results after the loop:
415
+ ```javascript
416
+ (steps, context) => {
417
+ const allOutputs = [];
418
+ for (let i = 0; i < 5; i++) {
419
+ allOutputs.push(steps[`http[${i}]`].output);
420
+ }
421
+ return allOutputs;
422
+ }
423
+ ```
424
+
425
+ Access individual iteration output: `steps["<ref>[0]"].output`, `steps["<ref>[1]"].output`, etc.
426
+
427
+ ---
428
+
429
+ ## Pattern J — PARALLEL with LIST Branches (not SUB_PROCESS)
430
+
431
+ **Use case:** Run multiple independent test suites or processing tracks concurrently within the same workflow.
432
+
433
+ Structure: `PARALLEL → [LIST branch_1, LIST branch_2, LIST branch_3, ...]`
434
+
435
+ Each LIST branch has its own `ref` and independent `children` steps. Unlike Pattern D (SUB_PROCESS fan-out), the branches are inline within the same workflow JSON.
436
+
437
+ ```json
438
+ {
439
+ "type": "PARALLEL",
440
+ "children": [
441
+ { "type": "LIST", "ref": "branch_1", "children": [...], "input": {} },
442
+ { "type": "LIST", "ref": "branch_2", "children": [...], "input": {} },
443
+ { "type": "LIST", "ref": "branch_3", "children": [...], "input": {} }
444
+ ],
445
+ "input": { "failIfAnyBranchFails": true }
446
+ }
447
+ ```
448
+
449
+ Use `"failIfAnyBranchFails": false` when branches are independent and partial failure is acceptable.
450
+
451
+ ---
452
+
453
+ ## Pattern K — Rate-Limit Aware Integration
454
+
455
+ **Use case:** External API may return HTTP 429 (rate limit). Mark the step optional and use a SWITCH to gracefully skip when rate-limited.
456
+
457
+ Steps:
458
+ 1. `INTEGRATION` (optional: true, errorPolicyName: "retry_ep") — call the external API
459
+ 2. `SWITCH` — check if the output contains a rate-limit error
460
+ - Branch `plan_expired` → `JAVASCRIPT` (return skip/pass message)
461
+ - Branch `plan_working` → `LIST` (continue with main logic)
462
+
463
+ SWITCH script for rate-limit detection:
464
+ ```javascript
465
+ (steps, context) => {
466
+ if (steps.get_all_tables.output.error &&
467
+ steps.get_all_tables.output.error.includes("HTTP 429 429 TOO_MANY_REQUESTS")) {
468
+ return "plan_expired";
469
+ }
470
+ return "plan_working";
471
+ }
472
+ ```
473
+
474
+ SWITCH responseMapping:
475
+ ```json
476
+ [
477
+ { "targetRef": "plan_expired", "value": "plan_expired" },
478
+ { "targetRef": "plan_working", "defaultBranch": true }
479
+ ]
480
+ ```
481
+
482
+ ---
483
+
484
+ ## Ref Naming Conventions
485
+
486
+ | Step type | Ref pattern | Example |
487
+ |---|---|---|
488
+ | INTEGRATION | `<name>_<n>` | `google_sheets_1`, `llm_claude_1`, `http_1` |
489
+ | JAVASCRIPT | same as `name` | `map_input_fields`, `format_output`, `assert_1` |
490
+ | PYTHON | same as `name` | `extract_content`, `delete_attachments` |
491
+ | NOOP | same as `name` | `job_config`, `settings` |
492
+ | SWITCH | `switch_<n>` | `switch_1` |
493
+ | PARALLEL | `parallel_<n>` | `parallel_1` |
494
+ | FOREACH | descriptive name | `foreachstep`, `foreach_users` |
495
+ | WAIT | `wait_<n>` | `wait_1`, `wait_2` |
496
+ | HTTP (native) | `http` or `http_<n>` | `http`, `http_1` |
497
+ | PERSISTED_STATE | `persisted_state_<n>` | `persisted_state_1` |
498
+ | LIST (branch) | descriptive kebab | `new-sheet`, `branch_1`, `plan_working` |
499
+ | EXIT | `exit_<n>` | `exit_1` |
500
+ | SUB_PROCESS | same as `name` | `send_sendgrid_mail` |
501
+
502
+ ---
503
+
504
+ ## Data Flow Cheatsheet
505
+
506
+ ```
507
+ context.input.<field> ← workflow API input
508
+ steps.<ref>.output.response.<field> ← native HTTP response body
509
+ steps.<ref>.output.statusCode ← native HTTP status code
510
+ steps.<ref>.output.result.<field> ← JAVASCRIPT / PYTHON / most INTEGRATION output
511
+ steps.<ref>.output.results.<field> ← LLM Claude, Redis, MongoDB output
512
+ steps.<ref>.output.<field> ← NOOP output (no .result wrapper)
513
+ steps.<ref>.output.spreadsheetId ← Google Sheets CREATE output
514
+ steps.<ref>.output.result.rows ← PostgreSQL / MySQL SELECT output
515
+ steps.<ref>.output.id ← Google Drive, Notion CREATE output
516
+ steps.<ref>.output.webViewLink ← Google Drive upload share URL
517
+ steps.<ref>.output.status ← HTTP integration response status code
518
+ steps.<ref>.output.messages ← Outlook readEmail output (array)
519
+ steps.<ref>.output.tables ← Airtable GET_TABLES output (array)
520
+ steps.<ref>.output.records ← Airtable LIST/CREATE output (array)
521
+ steps.<ref>.output.fields.<FieldName> ← Airtable READ single record
522
+ steps.<ref>.output.issues ← Jira SEARCH_ISSUES output (array)
523
+ steps.<ref>.output.transitions ← Jira GET_TRANSITIONS output (array)
524
+ steps.<ref>.output.comments ← Jira GET_COMMENTS output (array)
525
+ steps.<ref>.output.results ← Notion QUERY/SEARCH/LIST_USERS/GET_BLOCK_CHILDREN
526
+ steps.<ref>.output.results.result ← Redis GET (string), KEYS (array), EVAL result
527
+ steps.<ref>.output.results.count ← MongoDB COUNT
528
+ steps.<ref>.output.results.success ← MongoDB CREATE/DELETE_COLLECTION
529
+ steps["<ref>[0]"].output ← FOREACH iteration output (index notation)
530
+ steps.__self.id ← current step ID (inside scripts)
531
+ steps.__self.startTime ← WAIT step start time (milliseconds)
532
+ context.id ← workflow process/run ID
533
+ ```
534
+
535
+ ---
536
+
537
+ ## Pattern L — WHILE Loop with State Counter
538
+
539
+ **Use case:** Repeat a block of steps a dynamic number of times, tracking progress in state.
540
+
541
+ Structure:
542
+ ```
543
+ WHILE (whileloop.iteration < N)
544
+ └── LIST
545
+ ├── WAIT (throttle)
546
+ └── JAVASCRIPT (__statePut to update state)
547
+ ```
548
+
549
+ WHILE input:
550
+ ```javascript
551
+ (steps, context) => {
552
+ return whileloop.iteration < 5;
553
+ }
554
+ ```
555
+
556
+ WAIT inside loop (use WHILE step's `.updated` as base):
557
+ ```javascript
558
+ (steps, context) => {
559
+ return { "waitUntil": steps.my_while_step.updated + 1000 };
560
+ }
561
+ ```
562
+
563
+ State increment JAVASCRIPT:
564
+ ```javascript
565
+ (steps, context) => {
566
+ return {
567
+ "__statePut": {
568
+ "count": (context.state.count || 0) + 1
569
+ }
570
+ };
571
+ }
572
+ ```
573
+
574
+ Read state anywhere: `context.state.count`
575
+
576
+ ---
577
+
578
+ ## Pattern M — Error Policy Retry Check
579
+
580
+ **Use case:** A step is expected to fail on early attempts (error policy retries it), then succeed. Assert that it eventually passed after N retries.
581
+
582
+ Mark the step `optional: true` and set `errorPolicyName` to a retry policy. In the assertion step, check `steps.__self.executionList.length` or verify the step eventually returned a valid output.
583
+
584
+ ```javascript
585
+ // Inside the retrying step — fail until retry count >= 3
586
+ (steps, context) => {
587
+ const index = steps.__self.executionList.length;
588
+ if (index < 3) {
589
+ throw Error("Failed the step because index < 3");
590
+ }
591
+ return { "index": index };
592
+ }
593
+ ```
594
+
595
+ Assert on failed optional step's error message:
596
+ ```javascript
597
+ (steps, context) => {
598
+ const errorMessage = steps.fail_debug.output.error;
599
+ if (!errorMessage.includes("expected error text")) {
600
+ throw Error("Not returning expected error");
601
+ }
602
+ return { "allGood": true };
603
+ }
604
+ ```
605
+
606
+ ---
607
+
608
+ ## Pattern N — Variables and Secrets in HTTP Steps
609
+
610
+ Use `{{variables.*}}` for environment-specific config and `{{secrets.*}}` for credentials. Never hardcode URLs or tokens.
611
+
612
+ ```json
613
+ {
614
+ "type": "HTTP",
615
+ "input": {
616
+ "method": "GET",
617
+ "url": "{{variables.dev_server}}/api/resource/{{steps.create.output.response.namespace}}/{{steps.create.output.response.name}}",
618
+ "headers": {
619
+ "Authorization": "Bearer {{secrets.unmeshed_test_token}}"
620
+ }
621
+ }
622
+ }
623
+ ```
624
+
625
+ Body with mixed template refs:
626
+ ```json
627
+ {
628
+ "body": {
629
+ "type": "json",
630
+ "content": {
631
+ "name": "{{steps.create.output.response.name}}",
632
+ "namespace": "{{steps.create.output.response.namespace}}",
633
+ "config": "{{steps.create.output.response.config}}"
634
+ }
635
+ }
636
+ }
637
+ ```
@@ -0,0 +1,18 @@
1
+ # Process Run Debugging Guide
2
+
3
+ Use this sequence when a user asks why a process failed or produced the wrong output.
4
+
5
+ 1. Fetch the process run by id with step details.
6
+ 2. Identify process status and process-level output.
7
+ 3. Find the first failed step.
8
+ 4. Inspect that step's runtime input, output, and error.
9
+ 5. Inspect previous step outputs referenced by the failed step.
10
+ 6. Compare runtime output paths against the step output path contract.
11
+ 7. Explain the root cause in plain English.
12
+ 8. If the fix is clear, propose the exact workflow change.
13
+
14
+ Common HTTP-to-JavaScript issue:
15
+ - HTTP output lives at `steps.<http_ref>.output.response`.
16
+ - JavaScript/Python output lives at `steps.<js_ref>.output.result`.
17
+ - If a JavaScript/Python step after HTTP reads `steps.<http_ref>.output.result`, it will receive `undefined`.
18
+
@@ -0,0 +1,24 @@
1
+ # Process Run Schema
2
+
3
+ Use this contract when explaining or debugging an executed process.
4
+
5
+ Process run records can include:
6
+ - Process id
7
+ - Namespace
8
+ - Process definition name
9
+ - Version
10
+ - Status
11
+ - Trigger type
12
+ - Correlation id
13
+ - Request id
14
+ - Created/updated timestamps
15
+ - Input
16
+ - Output
17
+ - Step records when requested with full details
18
+
19
+ Debugging rules:
20
+ - Use the process id to fetch the run.
21
+ - Use full step details when the user asks for complete JSON, step output, failed step, or troubleshooting.
22
+ - Do not infer output from the process definition alone; inspect the executed run output.
23
+ - If process-level output is `null`, inspect step records before claiming the workflow produced no useful data.
24
+