CDX Integration
This page maps the current workflow packages in CDX to the runtime, storage, template, feature, protocol, and editor surfaces.
Server Runtime
A production service wires four things:
- A
WorkflowStorageimplementation. - A
WorkflowTypeResolver. ExecutionMode.production.- Workflow definitions loaded into the engine memory cache.
import 'package:cdx_workflow_templates/cdx_workflow_templates.dart';
import 'package:supabase/supabase.dart';
import 'package:vyuh_workflow_engine/vyuh_workflow_engine.dart';
import 'package:vyuh_workflow_storage_supabase/vyuh_workflow_storage_supabase.dart';
final approvalTemplate = ApprovalTemplate(
domains: [equipmentApprovalDomain, materialApprovalDomain],
);
final context = RegistryTypeResolver(
descriptors: [
DefaultWorkflowDescriptor(),
approvalTemplate.buildDescriptor(),
],
);
final storage = SupabaseStorage(
client: SupabaseClient(url, serviceRoleKey),
config: const SupabaseStorageConfig(schema: 'elog'),
);
final engine = WorkflowEngine(
context: context,
storage: storage,
executionMode: ExecutionMode.production,
);
await engine.initialize();
final storedApproval = await engine.storage.workflows.getByCode(
approvalTemplate.workflowCode,
);
if (storedApproval == null) {
throw StateError('Missing seeded approval workflow definition.');
}
engine.loadWorkflowDefinition(storedApproval);For local tests, replace SupabaseStorage with InMemoryStorage(). If you need a local seed, create the template definition through storage.workflows.create(approvalTemplate.buildDefinition()) before loading it.
Canonical Approval Template
cdx_workflow_templates currently provides the canonical shared approval workflow.
| Concept | Current contract |
|---|---|
ApprovalTemplate | A WorkflowTemplate that bundles the canonical approval definition and descriptor. |
ApprovalWorkflow.code | Stable workflow code: approval. |
ApprovalWorkflow.buildDefinition() | Emits canonical JSON for seeding. Runtime should load the stored definition, not rebuild it as mutable application state. |
ApprovalWorkflow.buildDescriptor(...) | Registers approval service-task and user-task executors. |
ApprovalDomain | Per-domain typed slots for item/config/decision JSON, chain resolution, outcome effects, and optional refreshItem. |
item_schema_type | Dispatch key in ApprovalWorkflowInput.toJson() used to select the correct ApprovalDomain. |
The approval graph is versioned in ApprovalWorkflow.buildDefinition(). Version 4 includes failure-port routing to on_failed, revision-loop nodes, and stable enum-backed node/port IDs from cdx_workflow_types.
Approval Input
Start approval instances with the typed input envelope from cdx_workflow_types:
final input = ApprovalWorkflowInput(
item: item,
config: approvalConfig,
submittedBy: userId,
submittedAt: DateTime.now().toUtc(),
correlationId: item.id,
);
final instance = await engine.startWorkflow(
workflowCode: ApprovalWorkflow.code,
input: input.toJson(),
correlationId: item.id,
userId: userId,
);The top-level item_schema_type is required. Dispatcher executors use it to select the registered domain before they deserialize the item, config, or decision payload.
Supabase Storage
vyuh_workflow_storage_supabase implements WorkflowStorage.
Default configuration:
const SupabaseStorageConfig(
schema: 'elog',
definitionsTable: 'workflows',
instancesTable: 'workflow_instances',
userTasksTable: 'workflow_user_tasks',
eventsTable: 'workflow_events',
);The adapter verifies all four tables during initialize(). It does not create tables and it does not perform type resolution. Stored workflow rows are WorkflowDefinition data; the engine converts them into executable Workflow objects through its resolver.
Feature Inbox
cdx_feature_workflows is the Flutter/Vyuh inbox surface for workflow user tasks. It exports:
WorkflowActionBar, driven by server-providedList<UserTaskAction>.WorkflowStatusCardfor entity workflow status.- Inbox row/detail helper widgets.
WorkflowActivityTimelineViewand labels.entityWorkflowDataFromTaskfor a first-pass task-to-status projection.CdxFeatureWorkflowsPluginandfeature.
Task actions are resolved server-side through UserTaskActionResolverRegistry and serialized on the task projection. The Flutter action bar switches on the sealed UserTaskAction subtype to render icons, severity, and labels; it does not infer approval semantics from schema strings.
Protocol Client
vyuh_workflow_protocol wraps engine interaction in typed messages:
- Commands: start, signal, cancel, fail, retry, state, subscribe, unsubscribe, pause/resume.
- Responses: command success/error/state envelopes.
- Events: workflow events, engine errors, heartbeat.
- Transports:
WorkflowTransportplusInProcessTransport.
Use WorkflowProtocolClient when a client or editor should not call WorkflowEngine directly:
final transport = InProcessTransport(engine: engine);
final client = WorkflowProtocolClient(transport: transport);
await client.connect();
final instance = await client.startWorkflow(
workflowCode: 'approval',
input: approvalInput.toJson(),
);
await client.sendSignal(
workflowInstanceId: instance.id,
node: 'approval_decision',
port: 'approve',
payload: {'output': decision.toJson()},
);Visual Editor
vyuh_workflow_editor is Flutter-only. It exports the node editor SDK with workflow-specific hides, plus workflow models, controllers, validation, widgets, registry helpers, and run mode.
Typical embedding:
WorkflowEditor(
environments: [
SimulationEnvironment(
storageAdapter: InMemoryStorage(),
),
DatabaseEnvironment(
id: 'elog',
name: 'ELog',
storageAdapter: storage,
descriptors: [approvalTemplate.buildDescriptor()],
authConfig: authConfig,
),
],
initialEnvironment: 'simulation',
)Important editor behavior:
SimulationEnvironmentusesSimulationDeserializationContext,InMemoryStorage, passthrough task execution, and manual decision points.DatabaseEnvironmentuses real storage and descriptors, but editor execution mode is still simulation so users can drive branches, timers, subflows, and multi-output tasks manually.EditorContextcreates an in-processWorkflowEngine, wraps it inInProcessTransport, and exposes aWorkflowProtocolClient.- The editor library is loaded from
storage.workflows.list(activeOnly: true)and converted toWorkflowDocument.
Keep The Layers Separate
| Layer | Owns |
|---|---|
| Engine | Execution, tokens, effects, events, repositories, type resolution. |
| Supabase storage | Table-backed repository implementations only. |
| CDX types | Shared wire contracts and action/result/status models. |
| CDX templates | Canonical workflow definitions and descriptor/executor bundles. |
| CDX feature workflows | Flutter inbox/status/timeline widgets. |
| Protocol | Client/transport command and event API. |
| Editor | Visual authoring, validation, library loading, and simulation. |