elsa-core/doc/wiki/workflow-core.md

169 lines
9 KiB
Markdown
Raw Normal View History

# Workflow Core
Workflow core is the engine layer. It defines activities, execution contexts, scheduling primitives, inputs and outputs, variables, bookmarks, serialization, execution pipelines, flowchart behavior, and the core runner.
Start in [src/modules/Elsa.Workflows.Core](../../src/modules/Elsa.Workflows.Core).
## Feature Wiring
[WorkflowsFeature](../../src/modules/Elsa.Workflows.Core/Features/WorkflowsFeature.cs) registers the core services:
- `IActivityInvoker`
- `IWorkflowRunner`
- `IActivityTestRunner`
- `IActivityVisitor`
- `IWorkflowGraphBuilder`
- `IWorkflowStateExtractor`
- `IActivitySchedulerFactory`
- workflow and activity execution pipelines
- activity registry, descriptor, factory, and lookup services
- storage drivers
- serializers
- incident strategies
- UI hint handlers
- identity and hashing services
The feature also configures default workflow and activity pipelines. The umbrella [ElsaFeature](../../src/modules/Elsa/Features/ElsaFeature.cs) calls `WithDefaultWorkflowExecutionPipeline()` and `WithDefaultActivityExecutionPipeline()`.
## Activities
Core activity abstractions live in [Abstractions](../../src/modules/Elsa.Workflows.Core/Abstractions):
- `Activity`
- `Activity<T>`
- `CodeActivity`
- `WorkflowBase`
- `Behavior`
- `Trigger`
Built-in activities live in [Activities](../../src/modules/Elsa.Workflows.Core/Activities). Important families:
- Primitive control: `Sequence`, `If`, `Switch`, `For`, `ForEach`, `While`, `Parallel`, `Fork`, `Break`, `End`, `Finish`, `Complete`, `Fault`.
- Data and runtime helpers: `SetVariable`, `SetName`, `Correlate`, `WriteLine`, `ReadLine`.
- Dynamic and missing activity handling: `DynamicActivity`, `NotFoundActivity`.
- Flowchart: [Activities/Flowchart](../../src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities).
- State machine: [Activities/StateMachine](../../src/modules/Elsa.Workflows.Core/Activities/StateMachine/Activities). Named states with trigger-driven transitions. See [Activities And Authoring](activities-and-authoring.md) for the execution model.
Activities are described by `IActivityDescriber` and registered in `IActivityRegistry`. Workflow management adds activities to the available designer/API surface.
## Execution Contexts And State
Core execution state lives under [State](../../src/modules/Elsa.Workflows.Core/State) and [Models](../../src/modules/Elsa.Workflows.Core/Models). Important concepts:
- `WorkflowState`: serializable workflow execution state.
- `ActivityExecutionContextState`: serializable activity execution context state.
- `ActivityWorkItemState`: queued work item state.
- `WorkflowExecutionState`: high-level status and state model.
- `ActivityIncident`: fault or incident details.
- `WorkflowInput`: input passed into a workflow run.
- `ActivityOutputs` and `ActivityOutputRecord`: activity output capture.
Execution context extension tests live under [test/unit/Elsa.Workflows.Core.UnitTests/Extensions/ActivityExecutionContextExtensions](../../test/unit/Elsa.Workflows.Core.UnitTests/Extensions/ActivityExecutionContextExtensions).
## Inputs, Outputs, And Expressions
Inputs and outputs are modeled through:
- `Input<T>` and `Input`
- `Output<T>` and `Output`
- `InputDefinition`
- `OutputDefinition`
- `InputDescriptor`
- `OutputDescriptor`
- `Argument` and `ArgumentDefinition`
Expression handling bridges core workflows with language providers through [Expressions](../../src/modules/Elsa.Workflows.Core/Expressions) and the separate expression modules. `DefaultActivityInputEvaluator` evaluates inputs before activity execution.
## Scheduling Inside A Workflow
Core scheduling is about which activity work item runs next. Key services:
- [QueueBasedActivityScheduler](../../src/modules/Elsa.Workflows.Core/Services/QueueBasedActivityScheduler.cs)
- [StackBasedActivityScheduler](../../src/modules/Elsa.Workflows.Core/Services/StackBasedActivityScheduler.cs)
- [ActivitySchedulerFactory](../../src/modules/Elsa.Workflows.Core/Services/ActivitySchedulerFactory.cs)
- [WorkflowExecutionContextSchedulerStrategy](../../src/modules/Elsa.Workflows.Core/Services/WorkflowExecutionContextSchedulerStrategy.cs)
- [ActivityExecutionContextSchedulerStrategy](../../src/modules/Elsa.Workflows.Core/Services/ActivityExecutionContextSchedulerStrategy.cs)
feat(core): let a container withdraw work it scheduled but must not run (#7967) A container that schedules a child and then decides the child must not run had no way to withdraw it. `IActivityScheduler` exposed no removal operation, and `CancelActivityAsync` no-opped on a context whose status was `Pending`, so a container could tear a branch down and still have an activity from that branch execute afterwards, side effects and all. Fixes #7943. - `IActivityScheduler.RemoveWhere` removes work items and keeps the order the survivors would have been taken in; implemented in both the FIFO and LIFO schedulers. - `CancelActivityAsync` (both the public extension and the internal one used when a container completes) cancels `Pending` contexts as well as running ones, and withdraws the work item that would have started the cancelled activity plus the items it had scheduled for children with no context yet. Withdrawal is a real removal rather than a terminal status honoured at dequeue time, because the scheduler is also read: `Flowchart.HasPendingWork` inspects it to decide whether it may complete, and the work item list is extracted into the persisted workflow state — a withdrawn-but-queued item would be persisted and rehydrated with a fresh context after a suspend/resume. `StateMachine` had hand-rolled the same operation to drop competing triggers by clearing the scheduler and re-scheduling everything else; it now calls `RemoveWhere`. `Elsa.Bpmn` no longer needs to refuse a teardown whose subtree still has queued work, so `BpmnWorkTeardown` drops the `NotSupportedException` and records the teardown reason on the torn-down activity's journal instead. BREAKING: `IActivityScheduler` gains a member; external implementations must add `RemoveWhere`. Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:29:46 +00:00
Scheduling is reversible. A container that schedules a child and then decides the child must not run withdraws it by
cancelling: `CancelActivityAsync` cancels contexts that are still `Pending` as well as running ones, and removes from
the scheduler both the work item that would have started the cancelled activity and the work items it had scheduled for
children that have no execution context yet. Withdrawal is a real removal (`IActivityScheduler.RemoveWhere`) rather than
a flag honoured at dequeue time, because the scheduler is also read: `Flowchart` asks it whether the flowchart still has
pending work, and the work item list is part of the persisted workflow state.
Runtime scheduling and external dispatch are separate and live in `Elsa.Workflows.Runtime`.
## Bookmarks And Triggers
Core models define bookmark concepts:
- [Bookmark](../../src/modules/Elsa.Workflows.Core/Models/Bookmark.cs)
- [BookmarkInfo](../../src/modules/Elsa.Workflows.Core/Models/BookmarkInfo.cs)
- [CreateBookmarkArgs](../../src/modules/Elsa.Workflows.Core/Models/CreateBookmarkArgs.cs)
- [TriggerType](../../src/modules/Elsa.Workflows.Core/Models/TriggerType.cs)
The runtime persists and indexes bookmarks/triggers. Core activities create bookmarks and signals; runtime services decide how they are stored and resumed.
## Flowchart Execution
Flowchart support is split between:
- [FlowchartFeature](../../src/modules/Elsa.Workflows.Core/Features/FlowchartFeature.cs)
- [Flowchart activities](../../src/modules/Elsa.Workflows.Core/Activities/Flowchart/Activities)
- flowchart extension methods in [Activities/Flowchart/Extensions](../../src/modules/Elsa.Workflows.Core/Activities/Flowchart/Extensions)
Relevant ADRs:
- [ADR 0005: Token-Centric Flowchart Execution Model](../adr/0005-token-centric-flowchart-execution-model.md)
- [ADR 0007: Explicit Merge Modes For Flowchart Joins](../adr/0007-adoption-of-explicit-merge-modes-for-flowchart-joins.md)
## Pipelines
Core has separate workflow and activity execution pipelines:
```mermaid
flowchart LR
Runner["IWorkflowRunner"] --> WorkflowPipeline["IWorkflowExecutionPipeline"]
WorkflowPipeline --> Scheduler["Activity scheduler"]
Scheduler --> ActivityPipeline["IActivityExecutionPipeline"]
ActivityPipeline --> Invoker["IActivityInvoker"]
Invoker --> Activity["IActivity.ExecuteAsync"]
```
Pipeline extension methods live under [Extensions](../../src/modules/Elsa.Workflows.Core/Extensions) and middleware under [Middleware](../../src/modules/Elsa.Workflows.Core/Middleware). Pipelines are configured by `WorkflowsFeature`.
## Commit Strategies
Commit strategies determine persistence boundaries. Related files:
- [CommitStrategiesFeature](../../src/modules/Elsa.Workflows.Core/CommitStates/CommitStrategiesFeature.cs)
- [CommitStrategies](../../src/modules/Elsa.Workflows.Core/CommitStates)
- workflow sample configuration in [Elsa.Server.Web/Program.cs](../../src/apps/Elsa.Server.Web/Program.cs)
Runtime replaces the default no-op commit handler with an execution-cycle-aware handler so state changes are persisted at runtime boundaries.
## Serialization
Core serializers live under [Serialization](../../src/modules/Elsa.Workflows.Core/Serialization), including:
- `JsonWorkflowStateSerializer`
- `JsonPayloadSerializer`
- `JsonActivitySerializer`
- `ApiSerializer`
- `SafeSerializer`
- `StandardJsonSerializer`
Custom constructor and additional converter configurators are registered by `WorkflowsFeature`.
Refactor: Overhauls workflow JSON type serialization (#7549) * Avoid null endpoint DTO metadata in tests * Enforce console logs hub read permission * Remove unused console logs hub import * Support mapped endpoint metadata in auth tests * Reduce console log capture throughput impact * Address Copilot console logs review * Refactor task scheduling to support tenant-level background work and enhance logging functionality. * Introduce ConsoleStreamHook for stdout/stderr tee and enhance logging validation. Adjust test cases and startup warnings for distributed lock provider usage. * Refactor console logging pipeline with capture optimization and new ConsoleLogsHost; update tests accordingly. * Add Ansi SGR parser for console logs and associated unit tests * Remove ANSI color renderings and parsers; integrate ConsoleLogScopeAccessor for improved logging context with workflow instance ID support. * Address console logs code quality feedback * Address PR review feedback * Preserve console logs extension points * Stabilize console logs host lifecycle * Address final automated review comments * Tighten console log capture shutdown * Address console log review feedback * Address follow-up review feedback * Cover final review feedback * Avoid recursive console provider initialization * Guard console host lease shutdown * Preserve console log scope and provider lifetime * Correlate console log scope fallback * Tighten console scope correlation * Expose host services during provider construction * Redact ANSI-normalized console lines * Remove `ConsoleCaptureTee` and related services and tests * Use pipeline contributors for console log context * Update CShells package versions to 0.0.24-preview.132 * Filter live console logs by workflow instance * Enhance console logging with activity execution metadata and extend test coverage. * Address console logs stream consumption comment * Add diagnostics OpenTelemetry backend * Introduce dedicated workflow JSON type registry and hardening This change addresses GitHub issue #7541 by establishing a separate type registry (`IWorkflowJsonTypeRegistry`) for workflow JSON serialization. This decouples workflow type resolution from expression type aliases, enforcing a strict trust boundary. Key aspects: - New workflow JSON emits preferred aliases for registered types. - Existing persisted workflows can be loaded via registered legacy names. - Unknown, abstract, interface, open generic, or inappropriate collection types are rejected during deserialization, enhancing security. - Public APIs (e.g., incident strategies) now expose consistent workflow JSON type identifiers. This ensures secure, predictable, and backward-compatible handling of types within workflow definitions and payloads. * Remove unused project references and streamline console log endpoint * Move serialization type aliases to Elsa.Common * Update serialization integration fixtures for aliases * Stabilize missing rate limiter policy test
2026-05-31 09:09:39 +00:00
### Workflow JSON Type Identifiers
Workflow JSON type resolution uses the shared `ISerializationTypeRegistry` from `Elsa.Common.Serialization`, not expression type aliases. Register workflow-serializable payload types through `SerializationTypeOptions`; keep `ExpressionOptions` for expression/type metadata only.
New workflow JSON writes preferred aliases when a type is registered. Compatibility reads also accept explicitly registered legacy names, including selected CLR names from older persisted workflow JSON. Unknown CLR names are rejected rather than loaded dynamically. Polymorphic object reads also reject abstract, interface, open generic, and unsupported collection targets unless the resolver can map a known collection interface to a concrete collection type.
Public API payloads that expose workflow JSON type identifiers should emit values from `ISerializationTypeRegistry`. For example, incident strategy descriptors return the alias that workflow JSON accepts, while registered legacy CLR names remain readable during the compatibility window.
## When To Change This Layer
Change workflow core only when you are changing engine semantics, activity contracts, execution state, serialization, core activity behavior, or flowchart behavior. If the change is about persisted definitions, API DTOs, background dispatch, or a module-specific transport, start in management, API, runtime, or the extension module instead.