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.StepExecutionNodeEach 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).BranchExecutionNodebranchIdwhen: 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.WorkflowExecutionResultsuccess: 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 & generateKnownFieldsinferFieldType: 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.WorkflowContextstate: 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 |