Core Philosophy
The Vyuh Workflow Engine separates workflow shape, executable behavior, runtime state, and storage.
Token-Based Execution
Workflow instances move tokens through a graph. Tokens make parallel execution and recovery explicit:
Runtime state persists the active/waiting/consumed token positions. After a crash, the engine can find stale running instances and resume active tokens.
Definition vs Execution
The current model has two important shapes:
| Shape | Purpose |
|---|---|
WorkflowDefinition | Persisted definition data. Used by storage, templates, JSON, and the editor library. |
Workflow | Executable graph loaded by the engine. Node configs and processors are resolved before execution. |
This keeps storage simple and lets the engine choose the right resolver for production, simulation, or tests.
Descriptor-Based Logic
Workflow definitions carry schema types. Business logic lives in descriptors:
final descriptor = WorkflowDescriptor(
title: 'Email tasks',
tasks: [SendEmailTaskExecutor.typeDescriptor],
);
final context = RegistryTypeResolver(
descriptors: [
DefaultWorkflowDescriptor(),
descriptor,
],
);
final engine = WorkflowEngine(
context: context,
storage: InMemoryStorage(),
executionMode: ExecutionMode.production,
);
await engine.initialize();Benefits:
- Definitions stay serializable.
- Executors are reusable and unit-testable.
- Runtime can load definitions from storage without embedding executable code in JSON.
- Simulation can substitute passthrough executors without changing definitions.
Effect-Based Outputs
Executors do not mutate workflow state directly. They return results and effects:
return TaskSuccess([
SetOutputEffect(output: {'sent': true}, path: 'notification'),
RouteToPortEffect(portId: 'success'),
]);The engine processes effects centrally, which keeps persistence, event emission, token updates, user-task creation, and cancellation in one place.
Signal Coordination
External interactions resume workflows through signals:
await engine.sendSignal(
workflowInstanceId: instance.id,
node: 'approval_decision',
port: 'approve',
payload: {'output': decisionPayload},
);Signals can target node ID, schema type, or signal name. Ports can target output port IDs or labels.
Storage-Agnostic Runtime
The engine only depends on WorkflowStorage. CDX ships:
InMemoryStoragefor tests, demos, and editor simulation.SupabaseStoragefor Supabase/PostgreSQL deployments.
Custom adapters can implement the same repository interfaces.
Templates Over Repeated Graphs
For CDX approvals, use ApprovalTemplate rather than rebuilding similar approval graphs in each app. The canonical approval workflow handles:
- Multi-level chain execution.
- Approval, rejection, revision, resubmission, cancellation, and failure outcomes.
- Domain dispatch through
item_schema_type. - Typed server actions for Flutter inbox rendering.
Editor Is A Simulation Surface
vyuh_workflow_editor can load workflows from storage and descriptors, but editor execution runs in simulation mode so authors can manually drive decision points, timers, subflows, and multi-output routes.