Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneeszack

Relationships

#3307 A signal by key can be answered pending or taken when that is not what happened to it

Opened by zack · 10/12/2026· Shipped 10/12/2026

Problem

Follow-up to swamp-club#3212 (early signals, acceptEarly), raised by the adversarial and code reviews of PR 3021. Two replies of workflow signal by key can say something other than what happened to the signal.

1. pending for a signal the opening wait then refuses

When a signal arrives while a step is opening its wait (the key is claimed, the wait is not registered yet), arriveEarly in src/libswamp/workflows/signal.ts answers pending on the assumption that the step takes the stored record when it reads its slot (the held case with no registration).

pendingOutcome in src/domain/workflows/pending_signal.ts can still refuse it:

  • receivedAt is earlier than the claim's early.from: the gap between the signal and the claim exceeded acceptEarly. Easy to hit at small values.
  • The run captured a schema that differs from the current definition and refuses the payload.
  • authorizedFor differs from the workflow name the run recorded (a rename, or a copy that shares the ID).

The step then suspends with the signal in its own slot. No later claim reads that slot, and every reader of this wait applies the same refusal, so the signal is never taken and the wait runs to its deadline. The sender was told it is held.

2. taken always says no resume is needed

landedAnswer sets awaitingResume: false for a taken answer. findTakerOf only checks that the claim's outcome carries this signal's receipt, not who wrote the outcome. If another reader (workflow waits, or serve's takePendingSignalsOfRun) gives the pending signal to a wait that is already suspended, between the signaller's second read and findTakerOf, the reply is taken. The CLI prints "its run went on without waiting" and shows no resume command, though the run is suspended. Under swamp serve with auto-resume the run is continued anyway; locally the user is not told to resume.

Related: renderRemoteSignalResult tells a caller with signal alone "The signal takes effect when the run is next resumed" even for a signal the step took on opening, because the server sends taken only to callers who may read the workflow.

Suggested fix

  1. Before answering pending in the held-and-unregistered case, evaluate the stored record against the claim's early and the workflow name as pendingOutcome will. If it would not be taken, fall through to deliverToHolder or answer with a clear refusal.
  2. Compute decideContinuation(run, store) for taken as the duplicate answer already does, instead of hard-coding false. Either send taken to every caller (it names no run) or make the signal-only sentence neutral.

Add tests for each case; neither is covered today.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 7 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

10/12/2026, 9:30:24 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
zack assigned zack10/12/2026, 8:35:43 PM

Sign in to post a ripple.