Subprocesses¶
A subprocess node runs another workflow as a child and resumes when that
child completes. It is how a model is composed rather than duplicated: a
"collect the applicant's documents" flow written once is called from every
process that needs it, and the caller sees one step.
On reaching the node the engine starts a child instance and parks the parent token. Selected parent variables are passed in; when the child completes, the engine maps the child's variables back into the parent and resumes the parked token, so the outgoing flows route on what the child produced.
Because the parent parks like any other waiting step, everything that applies to a parked token applies here: timers can escalate or resume a parent whose child is taking too long, and a timeout that resumes the parent cancels the child still running under it.
Settings¶
n_collect:
type: subprocess
config:
process: collect_documents
input: |
applicant
case_reference: reference
output: |
documents
verdict: collection_verdict
completion_scope: instance
state_variable: collection_state
relaunch_attempts: 2
relaunch_backoff: PT10M
process: the child workflow's machine name. The modeler offers every workflow as a select.input: the parent variables to pass into the child, one per line. A barenamekeeps the name across the boundary;name: child_namerenames it.output: the child variables to inject back into the parent when it completes, in the same one-per-line form,nameorname: parent_name.completion_scope:instance(the default, shared by the whole process) ortoken(local to this branch and its descendants), so parallel branches each calling the same child keep their own results apart.state_variable: optional. Named, the parent resumes even when the child does not complete, with this variable set to__subprocess_failed__or__subprocess_canceled__so an outgoing flow condition can route the error. Left empty, the parent keeps waiting.relaunch_attempts: how many times a failed child is re-launched as a fresh child before the parent gives up and resumes with the failed state.0, the default, disables re-launching. Re-launches happen on cron, never inline.relaunch_backoff: optional delay before each re-launch, in seconds or as an ISO-8601 duration (PT10M). Empty re-launches on the next cron run, and the node form rejects a value it cannot measure with rather than letting a typo quietly remove the delay. Config written directly, bypassing that validation, falls back to no delay.
Each attempt is counted against the parent token, so re-entering the node (a loop) starts a fresh budget rather than inheriting a spent one.
What a child inherits¶
A child runs in its parent's tenant and carries the parent instance's initiator, so a subprocess never crosses a tenant boundary and "who started this" survives the hop.
Of the parent's variables, only the ones input names cross over: the child
does not see the rest, and its own writes reach the parent only through
output. A child start is an ordinary start otherwise, so the child workflow's
own declared variables are
seeded as well, with the parent's input winning where the two name the same
variable.
Execution and retention¶
Under synchronous execution a child is advanced inside the parent step's transaction, so the two commit together and a whole subtree drains in the one request. That whole subtree has to finish inside the advance lock's window, which is why a long-running child is a good reason to force queued execution for the caller. Under queued execution the child advances on its own cron turns while the parent stays parked.
A child instance is never purged while its parent is still running: it becomes eligible for retention once the parent finishes, and deleting an instance cascades to its subprocess children.