* fix: persist Interrupted after drain force-cancel commits Cancelled
Deadline-breach force-cancel makes the runner persist Finished/Cancelled.
#8059 then skipped every Finished row, so Interrupted never landed and
Packages CI failed DeadlineBreachPersistsInterrupted. Treat Cancelled as
interruptible and promote it to Running+Interrupted so recovery can
requeue it, while still refusing naturally completed rows.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: do not promote user cancellations to Interrupted on drain
Gate Cancelled→Interrupted on instances that were not already Cancelled
when drain snapshotted live cycles. Deadline-breach force-cancel still
promotes the runner's Finished/Cancelled commit; ordinary client
cancellations stay Cancelled and are not requeued.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: confine Cancelled→Interrupted promote to Drain
Restore TryMarkInterruptedAsync to refuse every Finished row by default
(#8052). Drain PersistInterrupted alone may pass allowFinishedCancelled
when the instance is Finished/Cancelled and in this drain's
force-cancelled set. User cancellations stay cancelled.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* docs: document TryMarkInterruptedAsync parameters
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: bound pre-cancel snapshot so stalled Find cannot block Cancel
WaitAsync the instance-store snapshot under a short shutdown budget so
a hang or ignored cancellation token cannot delay handle.Cancel().
Unknown pre-state is not treated as already Cancelled; observed
user cancellations are still preserved.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* test: restore Fact on terminal-race drain persist skip
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: exclude unknown snapshot rows from drain-induced promote
A timed-out or failed pre-cancel Find no longer joins drainInduced.
Only a successful read that is clearly not already Cancelled may be
promoted. Cancel still proceeds without waiting on store latency.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: give each pre-cancel snapshot its own timeout
A shared 250ms overallSnapshotCts let a stalled first Find cancel later
Finds before they started, so those instances were excluded from
drainInduced and never persisted as Interrupted after Phase A Cancel.
Each Find now uses an independent CTS linked only to the host token.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: snapshot live instances concurrently before Phase A Cancel
Independent per-find 250ms budgets kept recovery, but a serial foreach
still delayed every handle.Cancel by up to N×250ms. Run those bounded
Finds with Task.WhenAll so Cancel waits one timeout window, not N.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: give each parallel snapshot Find its own DI scope
Task.WhenAll was sharing one scoped IWorkflowInstanceStore. EF DbContext
is not thread-safe; Phase C already persists sequentially for that reason.
Each snapshot task now CreateScope()s its own store and disposes it.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* fix: stop requeuing Finished+Interrupted workflow instances
InterruptedRecoveryScanner now requires Status=Running, matching the
sibling crash-recovery task. DrainOrchestrator skips already-terminal
instances so a runner-clobber race cannot stamp Interrupted onto a
Finished row. Fixes#8052.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: conditionally mark Interrupted so drain cannot clobber Finished
PersistInterruptedAsync no longer SaveAsync-es the Find snapshot. The
store now applies Interrupted only when Status is still non-terminal
(EF: ExecuteUpdate WHERE Status != Finished; memory: mutate the live
row). A runner that commits Finished between read and write keeps its
terminal state, so startup recovery cannot requeue completed work.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* test: disambiguate NSubstitute Returns for TryMarkInterruptedAsync
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: make in-memory Interrupted mark atomic against completion
Lock the memory-store check and mutations together, then abort if Status
became Finished in-place so drain cannot record Interrupted on a
completed instance.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: share memory-store lock between Interrupted mark and Save
TryMarkInterruptedAsync now serializes with Save/Update/SaveMany so a
runner's terminal persist cannot land between the non-terminal check
and the Interrupted mutations. A finishing Save therefore cannot leave
Finished+Interrupted.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
* fix: register built-in activation strategy serialization aliases
3.8 type-resolution hardening no longer falls back to Type.GetType, so
Studio-selected and 3.7-era SQN activation strategy types failed to
deserialize. Register the four built-in strategies with legacy names
and advertise registry aliases from the descriptor endpoint.
Fixes https://github.com/elsa-workflows/elsa-core/issues/8049
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* test: DRY activation strategy serialization test setup
Extract shared registry/JSON arrange helpers so activation alias tests
share setup without collapsing their assertions.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* test: qualify Options.Create in serialization test helpers
Avoid Elsa.Options colliding with Microsoft.Extensions.Options.Options.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* test: keep activation alias setup local and obvious
Drop the shared Testing.Shared helper and extra Runtime wrapper. Those
hid one-line registry construction. Leave same-class helpers only.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
* fix: advertise resolvable activation strategy type names
When the registry has no preferred alias, fall back to the simple
assembly-qualified name so Studio stores an identifier the resolver
can load. FullName is not a registered alias.
Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Backport to release/3.8.0 so Elsa.Api.Client 3.8.0-rc2 exposes the
Resources/OutputConverters surface that Elsa Studio's release/3.8.0 branch
already consumes. Without it, Studio cannot build against a released client:
it was green against 3.8.0-preview.5397 (built from main) and broke when its
pin moved to 3.8.0-rc1 (built from this branch).
(cherry picked from commit d698e6b005)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds a new `Elsa.Http.Webhooks` module, enabling workflows to receive incoming webhook events and dispatch outgoing webhooks. This integrates the WebhooksCore library.
Further improvements include:
- Enhanced validation for configured application instance names, providing clearer feedback, especially regarding Azure Service Bus entity name limits.
- Improved API error reporting for shell reload operations, distinguishing between blueprint not found (404) and other failures (503).
- Updated release announcement rendering to dynamically reference the correct major.minor release line for feedback messages.
Use the same structural and secret-binding assessment for management, discovery, and initiation so incomplete overrides are never advertised as available sign-in methods.
Introduce a new test `ValidateRequiresCompleteConfigurationAndReturnsMissingSecretDetails` to verify that a connection requires a complete configuration, including handling missing secret details. Adjust configuration to enforce `RequiresClientSecret`.
Detach the AsyncLocal scope synchronously so notifications published after an asynchronous flush are not re-buffered and discarded. Add regression coverage that forces the flush across an async boundary.