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
docs(flow): document durable pause & resume (ADR-0019) in the flow guide
The flow guide covered ADR-0031 structured control flow but had nothing on
the suspend/resume runtime — approval/screen/wait pausing semantics, the
resume endpoint, wait-timer auto-resume + cold-boot re-arm, nested pause
through subflows (linked runs, #1693), and the runs observability API were
all undocumented (ROADMAP P1 "Document Flow-first automation").
Adds a "Durable pause & resume" section: per-node pause/resume table (with
the decide-through-approvals-API rule and why direct resume strands the
request), the nested-pause chain model with its showcase example pair, the
runs endpoints, and the in-memory-history caveat. Node-type table rows for
screen/wait/subflow now flag their pause behavior.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|`approval`| a human decision | the approvals service (`POST /api/v1/approvals/requests/:id/approve\|reject`) — resumes down the matching `approve` / `reject` edge. **Always decide through the approvals API**, never by calling `resume` directly: a direct resume strands the pending `sys_approval_request`, so the record stays locked and later approvals on the same record hit `DUPLICATE_REQUEST`. |
315
+
|`screen`| a user submits the form | the UI runner posting the collected `inputs`; a `paused` response carrying the next `screen` chains multi-step wizards under one stable `runId`|
316
+
|`wait` (timer) | an ISO-8601 duration elapses |**automatically** — a one-shot job resumes the run; after a cold boot the engine re-arms pending timers from the durable store (overdue timers resume immediately) |
317
+
|`wait` (signal) | a named external event | any caller invoking `resume(runId)`|
318
+
319
+
### Nested pause — pausing inside a subflow
320
+
321
+
A pausing node inside a `subflow` suspends the **whole chain as linked runs**:
322
+
the child run pauses at its node, and the parent pauses at the `subflow` node
323
+
(`correlation: 'subflow:<childRunId>'`). Both continuations are durable, and
324
+
the chain resolves from either end:
325
+
326
+
- deciding the child's approval (or its timer firing) completes the child and
327
+
**bubbles** its output back into the parent, which continues with the same
328
+
`${nodeId}.output` / `outputVariable` mapping as a synchronous subflow;
329
+
- resuming the **parent** run id (what a UI holds from the original launch)
330
+
**delegates** down to the suspended child — multi-screen child wizards keep
331
+
the parent run id stable.
332
+
333
+
A child that fails terminally after the pause fails every waiting ancestor, so
334
+
no run is stranded as resumable-forever. See the worked example pair in the
335
+
showcase app: `showcase_project_closure` invokes the reusable
336
+
`showcase_closure_signoff` approval subflow and notifies the owner with the
337
+
bubbled decision.
338
+
339
+
### Observing runs
340
+
341
+
```
342
+
GET /api/v1/automation/{flow}/runs # run history (status, steps, error)
343
+
GET /api/v1/automation/{flow}/runs/{runId} # one run
344
+
GET /api/v1/automation/{flow}/runs/{runId}/screen # re-fetch a paused screen
345
+
```
346
+
347
+
Each run's `steps[]` records every executed node — including loop iterations,
348
+
parallel branch bodies, and try/catch region steps — and the Studio flow
349
+
designer surfaces the same data in its **Runs** side panel. Run history is an
350
+
in-memory ring buffer (the durable rows in `sys_automation_run` hold only
351
+
*live pauses*), so history starts fresh on each boot.
352
+
299
353
## Edges
300
354
301
355
Edges connect nodes and define the execution path:
0 commit comments