doc/changelogs/3.6.0.md
Compare: 3.5.3...3.6.0
EF Core package names have changed: EF Core persistence packages were renamed from Elsa.EntityFrameworkCore.* to Elsa.Persistence.EFCore.*. If your application references any of the old package names, you must update them to the new package names when upgrading to 3.6.0. Be sure to review your project files, internal package feeds, CI pipelines, and deployment manifests for old package references.
Database migrations required (EF Core — all providers): ActivityNodeId columns in ActivityExecutionRecords and WorkflowExecutionLogRecords have been widened to unlimited types (nvarchar(max) / longtext / NCLOB) to support deeply nested workflows. The corresponding B-tree indexes (IX_ActivityExecutionRecord_ActivityNodeId, IX_WorkflowExecutionLogRecord_ActivityNodeId) are dropped as part of the V3_6 migrations. Run EF Core migrations before upgrading any SQL Server, MySQL, or Oracle deployment to 3.6.0. (71438596f3) (#7338)
Multitenancy — tenant ID convention change: An empty string ("") is now the canonical default tenant ID for all tenant-aware entities; null now means tenant-agnostic (visible to all tenants). EF Core query filters and the ActivityRegistry have been updated accordingly. If your database contains rows with a null TenantId that were intended to represent the default tenant, migrate those rows to "" before upgrading. The new NormalizeTenantId() extension method on string handles the conversion in code. (#7217, #7226)
Multitenancy — stored-trigger index updated: Database indexes on stored triggers now include TenantId. The V3_6 migration adds this index; no manual action is required beyond running migrations. (#7217)
DefaultRegistriesPopulator constructor change: The constructor now requires an additional INotificationSender dependency. If you manually instantiate or override DefaultRegistriesPopulator in tests or custom DI registrations, add the new parameter. Standard DI consumers are unaffected. (#7293)
Elsa.Common dependency trimmed: The DistributedLock meta-package (which pulled in all provider implementations) has been replaced with DistributedLock.Core. Applications that relied on the transitive provider packages (e.g. DistributedLock.SqlServer) being pulled in via Elsa.Common must add explicit package references. (53245cafbd) (#7169)
Flowchart merge mode renames: FlowJoin.MergeMode values have been renamed and expanded — None is now Stream, new modes Merge, Converge, Cascade, and Race are introduced. The default mode changed from None / counter-based to Stream / token-based. Existing workflows persisted with the old enum values may need a migration if the mode was explicitly set. See the Flowchart section for full descriptions. (730c01d9b0) (#6993)
TaskActivityAttribute removed: The [TaskActivity] attribute is no longer used to mark background-task activities. The ActivityDescriptor now exposes a RunAsynchronously property. If you reference TaskActivityAttribute in custom activity descriptors or tooling, replace it with the new property. (33af795704)
IActivityFactory marked obsolete: IActivityFactory and its related methods are marked [Obsolete] and will be removed in a future version. ActivityFactoryExtensions is provided for backward compatibility during migration. (8fd38a9949)
ElsaScript — textual workflow authoring DSL: A JavaScript-inspired Domain-Specific Language for authoring Elsa 3 workflows as code. ElsaScript compiles directly to the Elsa 3 Workflow object model at runtime, enabling workflows to be loaded dynamically from file systems, databases, or blob storage without a full recompile. It supports high-level constructs (flowchart, for, if/else, switch) that map 1:1 to their Activity counterparts, and mixed expression languages (JavaScript, C#, Liquid) within the same workflow. Use IElsaScriptCompiler / ElsaScriptParser to parse and compile .elsa files. (a1d4e541fc) (#7076).NET 10 in addition to .NET 8 and .NET 9. System.Linq.Async is added conditionally for compatibility on earlier TFMs. Project files have been normalized for cross-TFM consistency. (2f9eac110e) (#7062)FlowchartOptions / DI-configurable execution mode: Flowchart execution mode can now be selected per application using FlowchartOptions, configured via the DI builder or appsettings.json:
elsa.UseFlowchart(flowchart =>
{
flowchart.UseTokenBasedExecution(); // new default (3.6.0+)
// flowchart.UseCounterBasedExecution(); // legacy mode from ≤3.5.2
});
```json
{ "Elsa": { "Flowchart": { "DefaultExecutionMode": "TokenBased" } } }
```
Use `CounterBasedExecution` to keep the pre-3.6.0 behavior if existing workflow data was persisted under the old algorithm. ([4bbdd6f3a7](https://github.com/elsa-workflows/elsa-core/commit/4bbdd6f3a7)) ([#7141](https://github.com/elsa-workflows/elsa-core/pull/7141))
### Activity host registration
* **`HostMethodActivity` / activity hosts**: Register CLR types as **activity hosts** — public `async` methods are automatically discovered and exposed as individual activities in the workflow designer without manual boilerplate. New types: `HostMethodActivity`, `HostMethodActivityProvider`, `IHostMethodActivityDescriber`, `FromServicesAttribute`. ([fa04e1ebcd](https://github.com/elsa-workflows/elsa-core/commit/fa04e1ebcd)) ([#7172](https://github.com/elsa-workflows/elsa-core/pull/7172))
### Workflow dispatch notifications
* **Dispatch lifecycle notifications**: `BackgroundWorkflowDispatcher` now emits four new notifications at dispatch time, allowing subscribers to react to durable dispatch scenarios without a custom dispatcher:
* `WorkflowDefinitionDispatching` / `WorkflowDefinitionDispatched`
* `WorkflowInstanceDispatching` / `WorkflowInstanceDispatched`
([411ca0a332](https://github.com/elsa-workflows/elsa-core/commit/411ca0a332)) ([#7157](https://github.com/elsa-workflows/elsa-core/pull/7157))
* **`WorkflowDefinitionsReloaded` notification**: `DefaultRegistriesPopulator` now dispatches a `WorkflowDefinitionsReloaded` notification after repopulating the workflow definition store, enabling subscriber nodes to synchronize their registries. ([f5505d66c9](https://github.com/elsa-workflows/elsa-core/commit/f5505d66c9)) ([#7293](https://github.com/elsa-workflows/elsa-core/pull/7293))
### Consumers API & recursive export
* **`GET /workflow-definitions/{definitionId}/consumers`**: New endpoint returns all recursive consuming workflow definitions for a given definition. Built on a new `IWorkflowReferenceGraphBuilder` / `WorkflowReferenceGraph` model that replaces the previous ad-hoc recursive query approach. ([0a2e99ad42](https://github.com/elsa-workflows/elsa-core/commit/0a2e99ad42)) ([#7309](https://github.com/elsa-workflows/elsa-core/pull/7309))
* **Export with transitive consumers**: The export endpoint and `BulkExportWorkflowDefinitionsRequest` now accept an `IncludeConsumingWorkflows` flag (default `false`). When enabled, the ZIP export recursively resolves and includes all consuming workflows at their latest version. Exported filenames are now deterministic (`{Name}-{DefinitionId}.json`). ([0a2e99ad42](https://github.com/elsa-workflows/elsa-core/commit/0a2e99ad42)) ([#7309](https://github.com/elsa-workflows/elsa-core/pull/7309))
### Default commit strategies
* **Global default commit strategies**: New `WithDefaultWorkflowCommitStrategy()` and `WithDefaultActivityCommitStrategy()` extension methods on the workflows builder let you set application-wide fallback commit strategies. The resolution order for activities is: activity-specific → default activity → workflow-specific → default workflow → no commit. Default strategies are stored separately from the registry so they don't appear in the UI/tooling. ([31eff52d24](https://github.com/elsa-workflows/elsa-core/commit/31eff52d24)) ([#7148](https://github.com/elsa-workflows/elsa-core/pull/7148))
### Multitenancy enhancements
* **Null `TenantId` for tenant-agnostic entities**: Entities with a `null` `TenantId` are now treated as visible across all tenants. EF Core query filters and `ActivityRegistry` query methods include `null`-tenanted records in every tenant's scope. New `ActivityDescriptor.TenantId` (nullable) property added. ([7bc9035f5e](https://github.com/elsa-workflows/elsa-core/commit/7bc9035f5e)) ([#7226](https://github.com/elsa-workflows/elsa-core/pull/7226))
* **`TenantsOptions.IsEnabled` flag**: A new `IsEnabled` property on `TenantsOptions` allows tenant-specific logic to be conditionally applied. Tenant filters and `ActivityRegistry` queries now respect this flag. ([1b8083a5c6](https://github.com/elsa-workflows/elsa-core/commit/1b8083a5c6)) ([#7281](https://github.com/elsa-workflows/elsa-core/pull/7281))
* **Distributed locks for `Reload` / `Refresh`**: `IWorkflowDefinitionsReloader` and `IWorkflowDefinitionsRefresher` are now wrapped with distributed lock decorators to prevent multiple pods from concurrently executing reload or refresh operations. The refresher key incorporates definition IDs to allow concurrent refreshes of different definitions. ([689f19f2f2](https://github.com/elsa-workflows/elsa-core/commit/689f19f2f2)) ([#7311](https://github.com/elsa-workflows/elsa-core/pull/7311))
### Workflow instance export name provider
* **`IWorkflowInstanceExportNameProvider`**: A new injectable interface controls the filenames used when exporting workflow instances. The built-in provider preserves the existing GUID-based naming; override via DI to customize. ([3fd9f5b3af](https://github.com/elsa-workflows/elsa-core/commit/3fd9f5b3af)) ([#7146](https://github.com/elsa-workflows/elsa-core/pull/7146))
### Type conversion
* **`IEnumerableTypeConverter`**: A new `IEnumerableTypeConverter` handles conversion of non-generic `IEnumerable` sequences to typed collections, enabling smoother activity I/O interop. ([c017082f01](https://github.com/elsa-workflows/elsa-core/commit/c017082f01)) ([#7020](https://github.com/elsa-workflows/elsa-core/pull/7020))
---
## 🔧 Improvements
#### Flowchart / control flow
* **Flowchart merge modes expanded and clarified**: `FlowJoin.MergeMode` now includes five modes with well-defined semantics:
| Mode | Description |
| ------------ | ------------------------------------------------------------------------- |
| **Stream** | Opportunistic execution; ignores dead/untaken paths. New default. |
| **Merge** | Waits only for activated/flowing inbound branches. |
| **Converge** | Requires all inbound connections to complete. Strictest mode. |
| **Cascade** | Executes once per arriving token (allows multiple concurrent executions). |
| **Race** | Executes on first arrival; blocks others. |
([730c01d9b0](https://github.com/elsa-workflows/elsa-core/commit/730c01d9b0)) ([#6993](https://github.com/elsa-workflows/elsa-core/pull/6993))
* **`Fork` graceful no-branch completion**: The `Fork` activity now completes immediately when no branches are defined, rather than hanging. `BreakSignal` handling is simplified. ([3340e14b74](https://github.com/elsa-workflows/elsa-core/commit/3340e14b74)) ([#7104](https://github.com/elsa-workflows/elsa-core/pull/7104))
* **Flowchart execution modularity**: Flowchart activity execution logic is refactored for improved modularity and enhanced support for `FlowJoin` activity types. ([a80490101a](https://github.com/elsa-workflows/elsa-core/commit/a80490101a))
#### Distributed systems
* **`Elsa.Common` — distributed lock resilience**: A retry pipeline now wraps distributed lock acquisition and release to survive transient errors (network glitches, database timeouts). A new `ITransientExceptionDetector` service identifies retryable exceptions. ([ca268c16ad](https://github.com/elsa-workflows/elsa-core/commit/ca268c16ad)) ([#7161](https://github.com/elsa-workflows/elsa-core/pull/7161))
* **Race condition fix — duplicate trigger registration**: A race condition in the trigger indexing process allowed concurrent workflow engines to register identical HTTP endpoint triggers more than once, causing `"call is ambiguous"` routing errors. The fix adds proper synchronization and idempotency safeguards. Concurrent tests were added to prevent regression. ([fdf3e385b1](https://github.com/elsa-workflows/elsa-core/commit/fdf3e385b1)) ([#7131](https://github.com/elsa-workflows/elsa-core/pull/7131))
#### Workflow runtime
* **Workflow deletion robustness**: Workflow instance deletion now uses the workflow runtime for proper cancellation and cleanup of running instances. Exceptions during trigger deletion no longer prevent other workflows from loading. Thread safety added to the local task scheduler. ([aee09bb06f](https://github.com/elsa-workflows/elsa-core/commit/aee09bb06f)) ([#7088](https://github.com/elsa-workflows/elsa-core/pull/7088))
* **Tenant task manager with dependency ordering**: Tenant startup, background, and recurring task execution is now consolidated into a single `TenantTaskManager`. A new `[TaskDependency]` attribute and `TopologicalTaskSorter` ensure tasks execute in the correct dependency order. ([b577279321](https://github.com/elsa-workflows/elsa-core/commit/b577279321)) ([#7174](https://github.com/elsa-workflows/elsa-core/pull/7174))
* **`Result<T>` improvements**: `Result<T>` now supports strongly-typed operations and async handlers; `IsSuccess` is exposed for use in storage drivers and tests. ([1b8083a5c6](https://github.com/elsa-workflows/elsa-core/commit/1b8083a5c6)) ([#7281](https://github.com/elsa-workflows/elsa-core/pull/7281))
* **`ILoggerFactory` support in converters**: Converters can now receive `ILoggerFactory` to enable structured logging. ([9e6144922c](https://github.com/elsa-workflows/elsa-core/commit/9e6144922c)) ([#7153](https://github.com/elsa-workflows/elsa-core/pull/7153))
#### HTTP activities
* **`Content-Disposition` filename parsing**: `GetDownloadedFileNameOrDefault` in `DownloadHttpFile` now parses the `Content-Disposition` header (including quoted and extended filenames) before falling back to the URI. ([25575e65af](https://github.com/elsa-workflows/elsa-core/commit/25575e65af))
* **Graceful `JsonException` handling in `HttpEndpoint`**: Malformed request bodies no longer throw unhandled exceptions during workflow execution. ([d5efd38dcc](https://github.com/elsa-workflows/elsa-core/commit/d5efd38dcc))
#### Exports & serialization
* **Deterministic ZIP exports**: A fixed timestamp is applied to all ZIP archive entries, ensuring byte-for-byte reproducible exports across runs. ([87122082fd](https://github.com/elsa-workflows/elsa-core/commit/87122082fd))
* **`WorkflowTriggerEqualityComparer` serialization fix**: Type discriminator injection was incorrectly mutating the serialized payload used for trigger equality comparisons, causing spurious re-indexing. The comparer now uses a clean serialization path. ([a7e064abb0](https://github.com/elsa-workflows/elsa-core/commit/a7e064abb0)) ([#7320](https://github.com/elsa-workflows/elsa-core/pull/7320))
#### Multitenancy
* **`WorkflowDefinitionActivityProvider` tenant isolation**: Tenant ID is now included in the activity type cache key, preventing cross-tenant activity descriptor collisions in multi-tenant deployments. ([558902bb77](https://github.com/elsa-workflows/elsa-core/commit/558902bb77))
#### Misc
* **`GetSectionAsJson` null guard**: Returns `null` when the configuration section is absent, preventing `NullReferenceException`. ([2250e25e5c](https://github.com/elsa-workflows/elsa-core/commit/2250e25e5c))
---
## 🐛 Fixes
* **`Elsa.Workflows.Core` — `WaitAny` join ordering**: Inbound connection handling was refactored to **schedule the outbound activity before canceling remaining branches**, fixing a race condition where `WaitAny` could cancel the winning branch prematurely. ([d4b69be44b](https://github.com/elsa-workflows/elsa-core/commit/d4b69be44b)) ([#7340](https://github.com/elsa-workflows/elsa-core/pull/7340))
* **EF Core — `ActivityNodeId` column truncation on deeply nested workflows**: Fixed `String or binary data would be truncated` errors on SQL Server, MySQL, and Oracle for workflows with deep nesting (4+ levels). Columns widened; affected indexes dropped. Requires running V3_6 migrations. ([71438596f3](https://github.com/elsa-workflows/elsa-core/commit/71438596f3)) ([#7338](https://github.com/elsa-workflows/elsa-core/pull/7338))
* **`ActivityExecutionContext.TryGet` — `Literal` handling restored**: Restores the `Literal` special-case in `ActivityExecutionContext.TryGet(MemoryBlockReference)` that was inadvertently removed in 3.5.2. Activities that programmatically create `Input<T>` with `Literal` values no longer throw `InvalidOperationException`. ([37921b5ee0](https://github.com/elsa-workflows/elsa-core/commit/37921b5ee0)) ([#7075](https://github.com/elsa-workflows/elsa-core/pull/7075))
* **`Switch` activity execution**: Refactored `Switch` to track scheduled activities and correctly complete execution when all scheduled activities finish, fixing scenarios where the activity would hang or complete prematurely. ([fbe9ca64f0](https://github.com/elsa-workflows/elsa-core/commit/fbe9ca64f0)) ([#7151](https://github.com/elsa-workflows/elsa-core/pull/7151))
* **Workflow-as-activities lookup by `DefinitionId`**: The `WorkflowDefinitionActivityProvider` now searches for workflow-as-activities by both `DefinitionId` and `VersionId`, fixing a regression where activities using version-agnostic references were not found after a reload. ([48b574a411](https://github.com/elsa-workflows/elsa-core/commit/48b574a411)) ([#7149](https://github.com/elsa-workflows/elsa-core/pull/7149))
* **Workflow reload logic**: Fixed two issues in the reload pipeline — a `NullReferenceException` caused by `TriggerScheduler` returning a null payload (blocking other workflows from publishing), and the reloader only executing partial loading logic (causing workflow-as-activity versions to be unresolved). ([5816f6e3be](https://github.com/elsa-workflows/elsa-core/commit/5816f6e3be)) ([#7324](https://github.com/elsa-workflows/elsa-core/pull/7324))
* **Unknown activity metadata preservation**: When deserializing a workflow that contains an unresolvable activity type, the placeholder activity now retains the original `metadata` payload (layout, annotations, etc.), preventing loss of designer context during round-trips. ([7e2829ddcd](https://github.com/elsa-workflows/elsa-core/commit/7e2829ddcd)) ([#7323](https://github.com/elsa-workflows/elsa-core/pull/7323))
* **HTTP — `Content-Length` mismatch in `RawStringContent`**: Replaced the `StreamWriter`-based `RawStringContent` with a `ByteArrayContent`-based implementation to guarantee bytes written == `Content-Length`, fixing errors under bulk dispatch. ([472f7da5fb](https://github.com/elsa-workflows/elsa-core/commit/472f7da5fb)) ([#7027](https://github.com/elsa-workflows/elsa-core/pull/7027))
* **Memory leak — Zstd codec (`IronCompress`)**: `IronCompressResult` objects returned by `Iron.Compress()` and `Iron.Decompress()` in `Zstd.cs` were not being disposed, causing `ArrayPool` buffer leaks on every compression/decompression call. Both `CompressAsync` and `DecompressAsync` now dispose the result correctly. ([05d40e3a2a](https://github.com/elsa-workflows/elsa-core/commit/05d40e3a2a)) ([#7193](https://github.com/elsa-workflows/elsa-core/pull/7193))
* **Multi-tenancy — cross-tenant cache collision in `CachingWorkflowDefinitionService`**: Cache keys in `WorkflowDefinitionCacheManager` were not scoped by tenant, causing one tenant's workflow definitions to be served to another in multi-tenant deployments. All cache key methods now include a tenant prefix. Single-tenant setups are unaffected. ([856f7b40b4](https://github.com/elsa-workflows/elsa-core/commit/856f7b40b4)) ([#7318](https://github.com/elsa-workflows/elsa-core/pull/7318))
* **Multi-tenancy — code-first workflow tenant assignment**: The `DefaultWorkflowDefinitionStorePopulator` was assigning `DefaultTenantId` to code-first (CLR) workflow graphs instead of `Tenant.AgnosticTenantId`, causing `WorkflowGraph` lookups to fail after upgrading to 3.6.0-rc3. ([09d768791a](https://github.com/elsa-workflows/elsa-core/commit/09d768791a)) ([#7252](https://github.com/elsa-workflows/elsa-core/pull/7252))
* **Multi-tenancy — normalization and workflow definition loading**: Tenant ID normalization (`null` → `""`) applied consistently across the stack. `DefaultWorkflowDefinitionStorePopulator` now filters definitions by current tenant. ([b09a564812](https://github.com/elsa-workflows/elsa-core/commit/b09a564812)) ([#7217](https://github.com/elsa-workflows/elsa-core/pull/7217))
* **Multi-tenancy — `BackgroundWorkflowCancellationDispatcher` tenant headers**: Tenant headers are now forwarded when dispatching workflow cancellation in the background, fixing cancellation of suspended multi-tenant workflows. ([bc70beff12](https://github.com/elsa-workflows/elsa-core/commit/bc70beff12)) ([#7040](https://github.com/elsa-workflows/elsa-core/pull/7040))
* **Multi-tenancy — label endpoint permissions**: Label API endpoints now use the `ElsaEndpoint` base with `ConfigurePermissions`, matching the pattern used by other endpoints. Unused permission constants removed. ([72569702fe](https://github.com/elsa-workflows/elsa-core/commit/72569702fe)) ([#7205](https://github.com/elsa-workflows/elsa-core/pull/7205))
* **`ReadSyntheticInputs` — resilient error handling**: An unhandled exception thrown when a synthetic input's `typeName` is missing (e.g. when a workflow input name clashes with a reserved property like `metadata` or `customProperties`) no longer blocks publishing of other workflows. Exceptions are now collected and logged rather than propagated. ([b8228351ae](https://github.com/elsa-workflows/elsa-core/commit/b8228351ae)) ([#7199](https://github.com/elsa-workflows/elsa-core/pull/7199))
* **`ResumeBulkDispatchWorkflowActivity` — missing `ParentInstanceId`**: Graceful handling added for `ParentInstanceId` being absent, preventing `NullReferenceException` during bulk workflow dispatch resumption. ([641dd664d6](https://github.com/elsa-workflows/elsa-core/commit/641dd664d6))
* **Cron triggers stopping silently when delay is zero**: Cron triggers no longer stop when the next-fire delay calculation returns zero, preventing silent trigger death under certain schedule configurations. ([b0a6edf0c6](https://github.com/elsa-workflows/elsa-core/commit/b0a6edf0c6)) ([#7047](https://github.com/elsa-workflows/elsa-core/pull/7047))
* **Semaphore release logic in scheduled task execution**: Fixed incorrect semaphore release logic that could cause deadlocks in scheduled task execution. ([e2a1b83ec8](https://github.com/elsa-workflows/elsa-core/commit/e2a1b83ec8)) ([#7045](https://github.com/elsa-workflows/elsa-core/pull/7045))
* **`RunWorkflowUntilEndAsync` — inputs not passed**: Inputs to `RunWorkflowInstanceRequest` in `RunWorkflowUntilEndAsync` were not forwarded to the workflow instance. ([7214a0f6ed](https://github.com/elsa-workflows/elsa-core/commit/7214a0f6ed)) ([#7022](https://github.com/elsa-workflows/elsa-core/pull/7022))
* **`ParentWorkflowInstanceId` not set**: Fixed a regression where `ParentWorkflowInstanceId` was not populated when dispatching child workflows. ([6637be296f](https://github.com/elsa-workflows/elsa-core/commit/6637be296f)) ([#7029](https://github.com/elsa-workflows/elsa-core/pull/7029))
---
## 🧩 Developer-facing changes
* **`IWorkflowReferenceGraphBuilder` / `WorkflowReferenceGraph`**: New graph abstraction for querying workflow consumer relationships. Registered automatically via `WorkflowManagementFeature`. `WorkflowReferenceGraphOptions` exposes `MaxDepth` and `MaxDefinitions` guards. ([#7309](https://github.com/elsa-workflows/elsa-core/pull/7309))
* **`TaskDependencyAttribute` + `TopologicalTaskSorter`**: Decorate tenant task implementations with `[TaskDependency(typeof(OtherTask))]` to declare ordering requirements. The new `TopologicalTaskSorter` sorts tasks before execution. ([#7174](https://github.com/elsa-workflows/elsa-core/pull/7174))
* **`IHostMethodActivityDescriber` / `FromServicesAttribute`**: New contracts for the activity host feature. Decorate host method parameters with `[FromServices]` to resolve them from the DI container at execution time. ([#7172](https://github.com/elsa-workflows/elsa-core/pull/7172))
* **`ActivityDescriptor.TenantId`** is now nullable (`string?`). Providers must handle `null` to mean "visible to all tenants". ([#7226](https://github.com/elsa-workflows/elsa-core/pull/7226))
* **`ActivityDescriptor.RunAsynchronously`** (new property): Replaces the removed `TaskActivityAttribute`. Set to `true` on a descriptor to run the activity asynchronously as a background task. ([33af795704](https://github.com/elsa-workflows/elsa-core/commit/33af795704))
* **`IWorkflowDefinitionsApi.GetConsumersAsync`**: New client method added to `Elsa.Api.Client`. ([#7309](https://github.com/elsa-workflows/elsa-core/pull/7309))
* **`RunWorkflowResultAssertions` / `RunWorkflowResultExtensions`**: New xUnit-friendly helpers for asserting workflow test outcomes. `RunActivityExtensions` methods marked obsolete in favor of `WorkflowTestFixture`. ([fbe9ca64f0](https://github.com/elsa-workflows/elsa-core/commit/fbe9ca64f0)) ([#7151](https://github.com/elsa-workflows/elsa-core/pull/7151))
* **`IWorkflowInstanceExportNameProvider`**: New interface for customizing workflow instance export filenames via DI. ([#7146](https://github.com/elsa-workflows/elsa-core/pull/7146))
* **Extension methods refactored to instance methods** in core workflow modules. Improves encapsulation and code discoverability. Previously extension-based callers may need minor updates. ([0837930ec1](https://github.com/elsa-workflows/elsa-core/commit/0837930ec1)) ([#7089](https://github.com/elsa-workflows/elsa-core/pull/7089))
---
## 🧪 Tests
* Added comprehensive unit, integration, and component tests for core activities:
* **Control flow**: `Fork` ([#7041](https://github.com/elsa-workflows/elsa-core/pull/7041)), `FlowDecision` ([#7117](https://github.com/elsa-workflows/elsa-core/pull/7117)), `FlowJoin` ([#7031](https://github.com/elsa-workflows/elsa-core/pull/7031)), `Container` ([#7102](https://github.com/elsa-workflows/elsa-core/pull/7102)), `Sequence` ([#7103](https://github.com/elsa-workflows/elsa-core/pull/7103)), `ParallelForEach` ([#7120](https://github.com/elsa-workflows/elsa-core/pull/7120)), `Switch` ([#7151](https://github.com/elsa-workflows/elsa-core/pull/7151))
* **Lifecycle**: `Complete` ([#7108](https://github.com/elsa-workflows/elsa-core/pull/7108)), `End` ([#7109](https://github.com/elsa-workflows/elsa-core/pull/7109)), `Start` ([#7111](https://github.com/elsa-workflows/elsa-core/pull/7111)), `Fault` ([#7113](https://github.com/elsa-workflows/elsa-core/pull/7113)), `Finish` ([#7114](https://github.com/elsa-workflows/elsa-core/pull/7114)), `NotFoundActivity` ([#7115](https://github.com/elsa-workflows/elsa-core/pull/7115))
* **Utilities**: `Correlate` ([#7119](https://github.com/elsa-workflows/elsa-core/pull/7119)), `SetName` ([#7116](https://github.com/elsa-workflows/elsa-core/pull/7116)), `ReadLine` ([#7112](https://github.com/elsa-workflows/elsa-core/pull/7112))
* **Events**: `EventBase` ([#7090](https://github.com/elsa-workflows/elsa-core/pull/7090)), `PublishEvent` ([#7093](https://github.com/elsa-workflows/elsa-core/pull/7093))
* **Dispatch**: `ExecuteWorkflow` ([#7003](https://github.com/elsa-workflows/elsa-core/pull/7003)), `BulkDispatchWorkflows` ([#7026](https://github.com/elsa-workflows/elsa-core/pull/7026)), `DispatchWorkflow` ([#7035](https://github.com/elsa-workflows/elsa-core/pull/7035))
* **HTTP**: `HttpEndpoint` ([#7125](https://github.com/elsa-workflows/elsa-core/pull/7125)), `DownloadHttpFile` ([#7094](https://github.com/elsa-workflows/elsa-core/pull/7094)), `WriteFileHttpResponse` ([#7016](https://github.com/elsa-workflows/elsa-core/pull/7016))
* Added idempotency component tests for trigger indexing (`TriggerIndexingIdempotencyTests`) and unit tests for `WorkflowTriggerEqualityComparer`. ([#7320](https://github.com/elsa-workflows/elsa-core/pull/7320))
* Added 11 integration tests for default workflow and activity commit strategies with exact commit-count assertions. ([#7148](https://github.com/elsa-workflows/elsa-core/pull/7148))
* Added integration tests for `IEnumerable`-to-array projection via JavaScript expression execution and refined type inference in `ExpressionExecutionContextExtensions`. ([#7290](https://github.com/elsa-workflows/elsa-core/pull/7290))
* Added unit tests documenting and verifying reserved activity input keyword behavior (e.g. `metadata`, `customProperties`). ([#7180](https://github.com/elsa-workflows/elsa-core/pull/7180))
* Added unit tests for `WorkflowReferenceGraphBuilder` and component tests for Consumers API and export with consumers. ([#7309](https://github.com/elsa-workflows/elsa-core/pull/7309))
* Added resilience unit and component tests for distributed lock retry pipeline. ([#7161](https://github.com/elsa-workflows/elsa-core/pull/7161))
* Added regression tests for `WaitAny` join schedule-before-cancel ordering. ([#7340](https://github.com/elsa-workflows/elsa-core/pull/7340))
* Added concurrency tests to verify duplicate trigger registration is prevented. ([#7131](https://github.com/elsa-workflows/elsa-core/pull/7131))
---
## 🔁 CI / Build
* Replaced `DistributedLock` meta-package with `DistributedLock.Core` in `Elsa.Common.csproj` to remove unused transitive provider references. ([53245cafbd](https://github.com/elsa-workflows/elsa-core/commit/53245cafbd)) ([#7169](https://github.com/elsa-workflows/elsa-core/pull/7169))
* Refined GitHub Actions `packages.yml` branch matching to include all `release/*` branches and removed the `prereleased` condition from the package handling trigger. ([34ecf02c32](https://github.com/elsa-workflows/elsa-core/commit/34ecf02c32), [394f22b6e0](https://github.com/elsa-workflows/elsa-core/commit/394f22b6e0))
* Removed `--depth=1` from `git fetch` commands in `packages.yml` to ensure full history is available. ([34ecf02c32](https://github.com/elsa-workflows/elsa-core/commit/34ecf02c32))
* Added code coverage configuration and GitHub Actions deployment of coverage reports. ([8747151330](https://github.com/elsa-workflows/elsa-core/commit/8747151330))
* Updated Dockerfiles to `.NET 10.0-bookworm-slim` SDK and runtime images. ([cd0cd10fb1](https://github.com/elsa-workflows/elsa-core/commit/cd0cd10fb1))
---
## 📦 Dependencies
* **`AutoMapper`**: bumped to `16.0.0`. ([8b9070df09](https://github.com/elsa-workflows/elsa-core/commit/8b9070df09))
* **`Microsoft.Extensions.Http.Resilience` / `Microsoft.Extensions.Resilience`**: bumped to `10.1.0`. ([ba004b7572](https://github.com/elsa-workflows/elsa-core/commit/ba004b7572))
* **`Oracle.EntityFrameworkCore`**: added for `net10.0` and `net9.0` targets. ([8aab381bc5](https://github.com/elsa-workflows/elsa-core/commit/8aab381bc5))
* **`Scrutor`**: version updated across solution. ([471ae03266](https://github.com/elsa-workflows/elsa-core/commit/471ae03266))
* **`DistributedLock`** meta-package replaced by **`DistributedLock.Core`** in `Elsa.Common`. ([53245cafbd](https://github.com/elsa-workflows/elsa-core/commit/53245cafbd)) ([#7169](https://github.com/elsa-workflows/elsa-core/pull/7169))
---
## 📦 Full changelog
* **Trigger serialization fix** (`WorkflowTriggerEqualityComparer`): avoid type discriminator injection during equality comparison. ([6f53c26f24](https://github.com/elsa-workflows/elsa-core/commit/6f53c26f24))
* **`WorkflowExecutionContext`**: removed unused `ClearCompletionCallbacks` method; `correlationId` parameter made non-optional in the constructor. ([fa798b0a47](https://github.com/elsa-workflows/elsa-core/commit/fa798b0a47), [79a64e90fd](https://github.com/elsa-workflows/elsa-core/commit/79a64e90fd))
* **`AttributeUsage` targets** updated for `InputAttribute`, `OutputAttribute`, and `ActivityAttribute` to reflect correct usage scenarios. ([3778a14e54](https://github.com/elsa-workflows/elsa-core/commit/3778a14e54))
* **`ClrWorkflowsProvider`** refactored to remove tenant prefix logic, relying solely on the `TenantId` property. ([41409b156d](https://github.com/elsa-workflows/elsa-core/commit/41409b156d))
* Removed `ConvertNullTenantIdToEmptyString` migration and its designer file. ([ccd8268413](https://github.com/elsa-workflows/elsa-core/commit/ccd8268413))
* Tenant isolation enforced in `WorkflowDefinitionActivityProvider`; tenant ID included in activity type cache key. ([558902bb77](https://github.com/elsa-workflows/elsa-core/commit/558902bb77))
* Removed legacy API key and service management functionality from server projects. ([e6899ac32c](https://github.com/elsa-workflows/elsa-core/commit/e6899ac32c))
* Removed `Elsa.ServerAndStudio.Web` and related sample projects from solution. ([ecf5b390f1](https://github.com/elsa-workflows/elsa-core/commit/ecf5b390f1))
* Updated documentation to reflect .NET 10.0 support and remove deprecated external dependency references. ([7add1030c4](https://github.com/elsa-workflows/elsa-core/commit/7add1030c4))
* Added DeepWiki badge to README. ([b3ad57191a](https://github.com/elsa-workflows/elsa-core/commit/b3ad57191a))
* Null safety and compiler warning fixes across multiple modules. ([490c8a2c9e](https://github.com/elsa-workflows/elsa-core/commit/490c8a2c9e), [2c0b3da5de](https://github.com/elsa-workflows/elsa-core/commit/2c0b3da5de)) ([#7050](https://github.com/elsa-workflows/elsa-core/pull/7050), [#7051](https://github.com/elsa-workflows/elsa-core/pull/7051))