#8187·elsa-core

IStimulusDispatcher stack orphaned after #8177 — delete BackgroundStimulusDispatcher / DispatchStimulusCommand

Author: sfmskywalkerCreated Sep 20, 2026Updated Sep 20, 2026
Labelscoreprio lowtech debtelsa 3maintenancetriaged

Problem

After #8150 / PR #8177, in-workflow and API async events both route through IWorkflowDispatcher (DispatchTriggerWorkflowsRequest). The parallel IStimulusDispatcher stack appears to have no remaining production callers in elsa-workflows/*, but is still registered, documented, and mirrored by MassTransit (see companion extensions issue).

That is dead accidental complexity left over from the dual-entry era #8149 described — and #8149’s EventPublisher bool-fork evidence is now stale.

Evidence (main @ d339a9c)

What #8177 changed

EventPublisher.PublishAsync(..., asynchronous: true) now calls:

csharp
await workflowDispatcher.DispatchAsync(new DispatchTriggerWorkflowsRequest(...), ...);

not IStimulusDispatcher. Sync still uses IStimulusSender (correct).

API authenticated event trigger (Events/TriggerAuthenticated) passes asynchronous: true|false into the same IEventPublisher — so API async events also no longer hit BackgroundStimulusDispatcher.

Orphan stack still in DI / tree

Piece Status
IStimulusDispatcher Interface + wiki/spec refs only
BackgroundStimulusDispatcher Registered via WorkflowRuntimeFeature.StimulusDispatcher default
DispatchStimulusRequest / DispatchStimulusCommand / DispatchStimulusCommandHandler Only reached from BackgroundStimulusDispatcher
Unit tests for BackgroundStimulusDispatcher Exist; no in-repo production injectors found

Org-wide code search for IStimulusDispatcher hits only: core contracts/feature/service/docs/specs + extensions MassTransitStimulusDispatcher + gitbook architecture page. No activity, endpoint, or publisher injects it.

Docs drift

doc/wiki/workflow-runtime.md still says API-triggered asynchronous events use the regular background dispatcher / not IStimulusDispatcher for PublishEvent only — partially updated for PublishEvent, but the orphan stack and “API async → background dispatcher” story no longer match code.

Graceful-shutdown specs still name the wrong choke point

specs/002-graceful-shutdown/* still list IStimulusDispatcher as an execution-cycle choke point. If the interface is deleted, those specs need a one-line retarget to IWorkflowDispatcher Trigger/Resume (or whatever remains).

Why it matters

  • Maintainability: two async stimulus façades for one job; one has zero callers.
  • Correctness risk: MassTransit still wires a separate StimulusDispatcher bus path (extensions companion) that never sees TransactionalWorkflowDispatcher — inviting future regressions if someone “fixes” a caller by reinjecting IStimulusDispatcher.
  • Subtractive clarity: #8149 option “delete IStimulusDispatcher” is no longer a design debate — it is the observed steady state after #8177.

Proposed subtractive direction

  1. Delete (or quarantine behind obsolete + analyzer) the unused core stack: IStimulusDispatcher, BackgroundStimulusDispatcher, DispatchStimulusRequest/Response, DispatchStimulusCommand + handler, feature factory StimulusDispatcher, and its unit tests.
  2. Keep IStimulusSender (sync broker) + IWorkflowDispatcher Trigger/Resume (async / outbox path).
  3. Update wiki + graceful-shutdown specs to drop IStimulusDispatcher as a choke point.
  4. Coordinate with extensions: remove MassTransitStimulusDispatcher + DispatchStimulusRequestConsumer* (companion issue).
  5. Do not invent a replacement framework. Remaining #8149 work is optional: collapse Trigger vs Resume false dual on IWorkflowDispatcher only.

Not a duplicate of

  • #8149 — broader dual/triple-entry collapse (EventPublisher fork + Trigger/Resume false dual). This issue is the post-#8177 concrete delete of the now-orphaned leg; prefer close #8149 to “delete done + Trigger/Resume optional leftover” or narrow #8149 after this lands.
  • #8150 (closed) / #8177 — durability fix that created this orphan by moving callers onto IWorkflowDispatcher.
  • #8167 — outbox Channel delivery bypass (separate).
  • #8136 — obsolete inbox DI.

Milestone

Unset — Elsa 3 Issue Triage / Crew Lead place. Soft proposal: Backlog / tech-debt lane beside #8149; do not auto-assign Engineer.

Source: elsa-workflows/elsa-core