This document provides an extensive reference for how GLIDE workflows are executed at runtime. It covers execution flow, step-by-step processing, fork handling, input/output validation, durations and event timings, error handling, and more.
WorkflowExecutor
FlowexecuteWorkflow
API
Once a GLIDE Workflow is parsed (see the GLIDE Parser Manual), the runtime uses
a WorkflowExecutor
to execute each step in the declared order:
when
condition,
auth
, input
, calling an action handler, and validating outputs.
WorkflowExecutionResult
, which describes success/failure,
timing, and a nested record of StepExecutionNode
objects.
The runtime is orchestrated by executeWorkflow(context)
. This function:
durableExecutor
in the context for scheduling actions.WorkflowExecutor
and calls its execute()
method.WorkflowExecutionResult
.StepStatus
'pending'
: The step is not yet started.'running'
: The step is currently in progress.'completed'
: The step finished successfully.'error'
: The step encountered an error and halted.'skipped'
: The step’s when
condition was false, so it did not run.'cancelled'
: Execution was aborted or halted by a previous error/skip, preventing this step from
running.StepExecutionNode
Each executed step produces a node with:
kind
: 'linear' or 'fork'stepId
: The unique ID of the step.status
: One of the StepStatus
values above.error
: If status
is 'error', this contains a RuntimeError
describing the
failure.uses
/ when
: For linear steps, we store the original uses
string and
when
condition for clarity.
startTime
, endTime
, durationMs
: Timestamps for linear steps only.branches
: For a fork step, a list of BranchExecutionNode
with sub-steps.output
: For linear steps, the ActionResult
returned by the handler (optional).BranchExecutionNode
branchId
when
: The branch's condition (string form).status
: Branch-level status. If any sub-step fails, the branch is marked 'error'
.error
: If the entire branch fails, store the main RuntimeError
here.steps
: An array of StepExecutionNode
describing each sub-step in the branch.WorkflowExecutionResult
success
: true
if all top-level steps succeeded (or were skipped) without error,
false
if any error occurred.
error
: A top-level Error
if something prevented normal execution (unknown error,
runtime bug, etc.).wallClockTimeMs
: Elapsed real time from the first step’s start to the final step’s end.totalIOTimeMs
: Sum of durations for all linear steps only, ignoring forks’ overhead or
waiting branches.steps
: Array of StepExecutionNode
describing the top-level steps in the order they
were encountered. Fork steps contain nested branch data.WorkflowExecutor
Flow
The WorkflowExecutor
orchestrates top-level step iteration:
execute()
records a workflowStartTime
and sets status='running'
.workflow.steps
in insertion order:
'cancelled'
.executeStep
is called on each step.executeStep
branches depending on the step’s kind
:
executeLinearStep
for linear stepsexecuteForkStep
for fork stepsstatus
is 'completed'
if no error occurred,
or 'error'
otherwise.
Finally, the executor calculates wallClockTimeMs
and totalIOTimeMs
, returning the
WorkflowExecutionResult
.
executeLinearStep
handles a single linear step:
StepExecutionNode
with status='running'
and records startTime
.
getActionDefinition(step.uses)
). If missing,
STEP_DEFINITION_ERROR
is thrown.
inputDefinition
and outputDefinition
(if any). If mismatched, a
STEP_DEFINITION_ERROR
occurs.
when
expression, evaluate it (via context.gel.evaluate
). If false,
mark status='skipped'
and finish early.processStepInput
is called to expand input
fields (render templates,
enforce min/max, etc.).
auth
credentials might also contain references that are rendered.
config
, auth
, and
an abortSignal
to allow cancellations.ActionResult
, which may have results
(output fields) and
extra
data.
If required output fields are missing, HANDLER_ERROR
is thrown. Otherwise,
validateStepOutput
ensures the output matches the step’s OutputSchema
.
context.state[stepId]
and used to register new known references
in context.gel
(via contributeFields
), enabling subsequent steps to do $myStep.output.someField
references.status='completed'
, record endTime
, durationMs
.
If an error is thrown at any point, status='error'
is set, capturing the RuntimeError
.
executeForkStep
handles a kind='fork'
step:
StepExecutionNode
with status='running'
, no uses
, no
startTime
or endTime
(since forks themselves don’t produce direct output/time).
Instead, it has an array of BranchExecutionNode
.
when
expression. If false
, set status='skipped'
and mark sub-steps as 'cancelled'
.true
, run each sub-step in sequence. If a sub-step fails or is skipped, subsequent steps in
that branch are marked 'cancelled'
or effectively halted.when: false
), the entire fork step is
'skipped'
.
status='error'
for the fork step; otherwise 'completed'
.
Branches run in parallel from a code perspective via Promise.all
, but each branch’s steps are still
linear in nature within that branch.
Once the fork step is completed (or errored), no further steps appear at that same level (fork is terminal).
processStepInput
Each linear step defines input
fields in the DSL. The runtime uses processStepInput
to:
value
. If missing, throw VALIDATION_ERROR
.default
if present or leave it undefined.{{ ... }}
template references. Certain field types (e.g. password
) disallow
references entirely, causing VALIDATION_ERROR
if found.integer
must parse to a safe int, boolean
can parse string/number forms,
file
must parse as a valid URL, duration
can be numeric or string like
"5 hours"
(converted to ms).
min_length
, max_length
, pattern
,
min_size
, etc.).
If any constraint is violated, VALIDATION_ERROR
is thrown.
When done, processStepInput
returns a fully typed object suitable for passing to the
action handler.
If a single field fails, the entire step is aborted with RuntimeError
.
validateStepOutput
After an action completes, we may have results
in the returned ActionResult
.
If the step defines an OutputSchema
, the runtime checks:
type
(string
, number
, boolean
,
etc.).extendable
is set.
If this validateStepOutput
fails, a VALIDATION_ERROR
arises.
Otherwise, the parsed output is stored in context.state[stepId]
, and
new references (like $stepId.output.myField
) are added to the GelEngine
for subsequent steps to use.
inferFieldType
& generateKnownFields
inferFieldType
: A helper that guesses the FieldType
from a runtime value.
For arrays, it does array<elementType>
; for objects, map<type>
if all values
are the same type, or object
if mixed.generateKnownFields
: Called after an action returns output to register
$stepId.output.fieldName
as a known path in GelEngine
.
The runtime uses RuntimeError
for structured error reporting, which includes:
type
: e.g. 'VALIDATION_ERROR'
, 'STEP_DEFINITION_ERROR'
,
'UNKNOWN_ERROR'
, etc.
stepId
: Which step was processing, if relevant.details
: Additional JSON data for debugging.cause
: Underlying error, if any.
Steps that fail set their status
to 'error'
and store the RuntimeError
.
The top-level WorkflowExecutionResult
will have success=false
,
and subsequent steps are either cancelled
or not executed.
executeWorkflow
API
executeWorkflow(context)
is the primary entry point for running a fully parsed workflow:
function executeWorkflow(context: WorkflowContext): Promise<WorkflowExecutionResult>;
context.durableExecutor
is present, else CONFIGURATION_ERROR
is thrown.workflow.steps
is defined. If not, throws STEP_DEFINITION_ERROR
.WorkflowExecutor
with the context
and calls execute()
.WorkflowExecutionResult
, containing hierarchical step data.WorkflowContext
state
: A record containing env
plus event data (file
or
payload
),
and any outputs from steps once they run.
gel
: A GelEngine
instance for evaluating when
conditions or rendering
templates during input processing.workflow
: The ParsedWorkflow with all step definitions.durableExecutor
: Implementation for executing tasks, backoff, timeouts, etc.import { parseWorkflow } from "./parser"; import { executeWorkflow } from "./runtime"; import { createWorkflowEvent } from "./schemas/events"; // 1) Build a minimal workflow DSL const rawWorkflow = { name: "GreetingsFlow", id: "0b84288d-a7b8-4dc6-bb67-d1154b9847c0", compatibility: "2025-01-30T00:00:00.000Z", trigger: { on: "manual" }, steps: { greet: { kind: "linear", uses: "myOrg/greet@v1", label: "Greeting Step", input: { extendable: false, fields: { name: { label: "Name", help: "Who to greet", type: "string", required: true, value: "GLIDE" } } } } } }; // 2) Create a 'manual' event const event = createWorkflowEvent("manual", { data: { userId: 123 } }); // 3) Parse the workflow => get a WorkflowContext const context = parseWorkflow(rawWorkflow, event); // 4) Execute the workflow executeWorkflow(context) .then((result) => { console.log("Success:", result.success); console.log("Execution Steps:", result.steps); }) .catch((err) => { console.error("Runtime error:", err); });
// Assume parseWorkflow gave us 'context' with a top-level fork step executeWorkflow(context).then(result => { if (!result.success) { console.error("Workflow failed. Error:", result.error); } else { console.log("All branches completed. Steps detail:", result.steps); } });
Reference | Description | Link |
---|---|---|
GLIDE Parser Manual | Explains how workflow JSON is validated and references are extracted. | Open |
actions.ts | Implementation details for action handlers and the actionBuilder approach. |
View Code |
runtime.ts | Source code for WorkflowExecutor , executeWorkflow , and related logic. |
View Code |