* 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.
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.
Document why activity execution context taint is cleared after commit instead of in the log sink, and refactor commit handler tests to share repeated fixture setup.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>