The Process
A Process is the outermost execution container. It coordinates one or more Threads, accumulates their outputs, and manages top-level state. A Process corresponds to a complete business workflow — "Onboard Employee", "Process Invoice", "Review Request".
What Is a Process?
In the data model, a Process is a database entity that owns a ProcessDefinition — a tree of configuration that describes what threads exist and what nodes each thread contains.
At runtime, a ProcessExecutionContext is created to track the live
state of a single execution run. This context is separate from the definition — the
same process definition can be running as many parallel execution contexts simultaneously.
ProcessDefinition
The static, versioned blueprint. Loaded once from the database and cached. Contains all thread definitions, element definitions, and connector bindings.
ProcessExecutionContext
The live execution state. Created fresh per run. Holds ExecutorID, start time, current state, and accumulated output from all threads.
Execution Flow
Step-by-Step
Clean up stale pause signals
Before anything else, any orphaned pause signal from a previous failed execution is removed. Stale signals can cause a healthy new execution to immediately pause — cleaning them first prevents this.
Start the execution context
processExecutionContext.Start(executorID) stamps the start time, sets state to Running, and initializes output tracking. The ExecutorID encodes ProcessID and AppID so every piece of execution data knows what it belongs to.
Load the ProcessDefinition
The definition is loaded via IProcessDefinitionService.LoadProcessAsync. If already loaded and attached to the operating context (as happens in resume scenarios), the cached copy is used.
Raise ProcessStarting event
The event system fires ProcessStarting. Any registered IExecutionEventHandler implementations receive this notification. Typical uses: logging, telemetry, audit trails, signaling the UI.
Check: are there any threads?
If the process has no active threads, execution ends immediately with state Completed. This protects against empty process definitions being silently stuck.
Execute threads — sequentially
Each thread is executed one at a time, in definition order. Before each thread, three checks are made:
- Cancellation — if the cancellation token is signalled, the process stops with state
Cancelled. - Webhook filter — if triggered by a webhook targeting a specific thread, all other threads are skipped.
- Pause signal — if a pause was requested externally, the process pauses after the current thread. The pause signal is then reset.
Accumulate thread output
After each thread completes, its output data is merged into the process context. To avoid key collisions when multiple threads produce data, each thread's output key is prefixed with thread_{threadID}_.
Fail-fast on thread failure
If any thread finishes with a non-Completed status, the process stops processing further threads and marks itself Failed. This is intentional — downstream threads may depend on the failed thread's outputs.
Raise ProcessCompleted event & finalize
The ProcessCompleted event fires. State transitions to Completed. Execution state is cleaned up. ProcessExecutionResultHelper.CloneFromContext snapshots the final context into an immutable result which is returned to the caller.
Process State Machine
| State | Meaning | Can transition to |
|---|---|---|
Running | Actively executing threads | Completed, Failed, Cancelled, Paused |
Completed | All threads finished successfully | — |
Failed | A thread failed or threw an unhandled exception | — |
Cancelled | CancellationToken was signalled | — |
Paused | Pause signal was received mid-execution | Running (on resume) |
Webhook Filter
When a process is triggered by an incoming webhook, only the thread that owns
the webhook trigger node should activate. The WebhookFilter on the
execution context carries a ProcessThreadID and a StartFromProcessElementID.
- At the process level: all threads whose
ProcessThreadIDdoes not match the filter are skipped entirely. - At the thread level: the execution stack is seeded with only the targeted entry node instead of all trigger nodes in the thread.