Flow Execution Engine — Saga Compensation and Retry
The engine that makes SysMARA flows actually run — not just validate
What is the Flow Execution Engine?
The Flow Execution Engine is the runtime that takes a FlowSpec and actually executes it — step by step, capability by capability. It handles the full lifecycle: context threading between steps, retry with exponential backoff, condition evaluation, and saga compensation when things go wrong. Every state transition is recorded as structured JSON for AI consumption.
This is what makes flows real. Without the executor, flows are just declarations. With it, they become executable workflows with built-in resilience.
Core Design Principles
- Steps are capabilities — each
step.actionmaps to a declared CapabilitySpec. The executor validates this at construction time. - Context threading — the output of step N is available as input to step N+1 via
context.stepOutputs. - Saga compensation — if a step fails with
onFailure: "compensate", the executor runs compensation steps in reverse order for all previously completed steps. - Retry with exponential backoff — steps with
onFailure: "retry"are retried up tomaxRetriestimes with exponentially increasing delays. - Safe condition evaluation — step conditions are evaluated using a recursive descent parser. No
eval()is ever used. - Structured execution log — every execution produces a
FlowExecutionRecordwith AI-readable summaries.
FlowExecutor API
import { FlowExecutor } from '@sysmara/core';
const executor = new FlowExecutor(specs, {
maxRetries: 3, // default: 3
retryBaseDelayMs: 100, // default: 100ms
capabilityHandler: async (capability, input, context) => {
// Your logic to execute a capability
return myService.run(capability, input);
},
});
// Execute a flow
const result = await executor.execute('user_signup_flow', {
email: 'alice@example.com',
role: 'admin',
});
// Validate a flow (check all capabilities exist)
const validation = executor.validate('user_signup_flow');
// List all registered flows
const flows = executor.listFlows();
// Access execution history
const log = executor.getExecutionLog();
const summary = log.summarize(); Capability Handler
The capabilityHandler is the pluggable function that actually runs capabilities. This design allows:
- Testing — provide an in-memory handler for unit tests
- Production — wire up to real databases and services
- Logging — the default handler logs invocations without side effects
type CapabilityHandler = (
capability: string,
input: Record<string, unknown>,
context: FlowContext,
) => Promise<unknown>; Execution Record
Every flow execution produces a FlowExecutionRecord — a complete, machine-readable record of what happened:
interface FlowExecutionRecord {
id: string; // unique execution ID
flowName: string;
status: FlowStatus; // "completed" | "failed" | "compensated"
triggeredBy: string;
input: Record<string, unknown>;
output?: Record<string, unknown>;
steps: StepExecutionRecord[]; // detailed record per step
error?: FlowError;
startedAt: string; // ISO timestamp
completedAt?: string;
totalDurationMs: number;
summary: FlowSummary; // AI-readable summary
} AI-Readable Summary
Each execution record includes a FlowSummary designed for AI agent consumption:
interface FlowSummary {
stepsTotal: number;
stepsCompleted: number;
stepsSkipped: number;
stepsFailed: number;
compensationsRun: number;
capabilitiesInvoked: string[];
entitiesAffected: string[];
} Saga Compensation
When a step with onFailure: "compensate" fails, the executor enters compensation mode:
- Identifies all previously completed steps that declare a
compensationcapability - Runs compensation capabilities in reverse order
- Records each compensation result in the execution record
- Sets final status to
"compensated"if all compensations succeeded, or"failed"if any compensation also failed
flows:
- name: user_signup_flow
trigger: user_signup
module: auth
steps:
- name: create_user
action: create_user
onFailure: compensate
compensation: delete_user # runs if later step fails
- name: create_profile
action: create_profile
onFailure: compensate
compensation: delete_profile # runs if later step fails
- name: send_email
action: send_welcome_email
onFailure: compensate # if this fails:
# 1. delete_profile runs (reverse of step 2)
# 2. delete_user runs (reverse of step 1) Condition Evaluation
Steps with a condition field are only executed if the condition evaluates to true. The evaluator supports:
- Property access:
context.input.role,context.stepOutputs.create_user.id - Comparison operators:
===,!==,>,<,>=,<= - Logical operators:
&&,|| - Literals: strings, numbers, booleans,
null,undefined
steps:
- name: admin_setup
action: setup_admin_dashboard
onFailure: abort
condition: 'context.input.role === "admin"'
- name: premium_onboarding
action: setup_premium
onFailure: skip
condition: 'context.stepOutputs.create_user.tier === "premium"' Execution Log
The FlowExecutionLog stores all execution records and provides aggregate summaries:
const log = executor.getExecutionLog();
// Query individual executions
const record = log.get('execution-id');
// Get all executions
const all = log.list();
// AI-readable summary of all executions
const summary = log.summarize();
// {
// totalExecutions: 42,
// successRate: 0.95,
// averageDurationMs: 150,
// mostFrequentFlows: [{ name: 'user_signup_flow', count: 30 }],
// recentFailures: [...]
// } CLI Commands
| Command | Description |
|---|---|
sysmara flow list | List all flows with step counts and triggers |
sysmara flow validate <name> | Validate a flow — check all step capabilities exist |
sysmara flow run <name> --input <json> | Execute a flow with JSON input (uses default logging handler) |
sysmara flow log | Show execution log summary |
Flow Validation
Before executing a flow, you can validate it to check that all step actions map to existing capabilities:
const result = executor.validate('user_signup_flow');
// {
// valid: true,
// errors: [],
// warnings: [],
// stepCount: 3,
// capabilitiesRequired: ['create_user', 'create_profile', 'send_welcome_email']
// }