Overlap is judged against the workflow's own unfinished runs
The overlap decision is made unattended, and a wrong answer is either a duplicated production run or a silently dropped one. The four policies are four genuinely different behaviours, and the buffer is a one-slot mailbox rather than a queue.
The rule
- Overlap is decided against runs of the same workflow that have not reached a terminal state: pending, running or paused.
- A terminal run, and a run of any other workflow, are both ignored; with none active, every policy proceeds with the caller's trigger data unchanged.
- With one active, Skip blocks the firing; Buffer defers it and blocks; Cancel cancels every active run, announcing each cancellation, and proceeds; Terminate first cancels every job those runs still have outstanding, so no worker can pick one up after the run is gone, and then does what Cancel does.
- The buffer holds at most one deferred firing per trigger: a second firing while one is buffered is dropped, never stacked and never overwritten.
- A buffered firing is released at the next firing once the run that blocked it is terminal or gone, and on release its own payload replaces the current firing's; a buffer whose blocking run can no longer be identified is released rather than left stranded.
- A policy an implementation does not recognise must be treated as Skip, the only fail-safe direction, since every alternative destroys a running workflow.
What it means
The buffer is a mailbox with one slot, not a queue: a second firing that arrives while one is already held does not stack behind it and does not replace it — it is simply dropped, and the first firing's data is what the mailbox still holds. A reader expecting FIFO behaviour, or expecting the newest firing to win, gets neither.
Release carries the same surprise the other way. When the blocking run ends and the buffered firing is let through, it runs with the payload it was holding when it was deferred — not with whatever data the firing that triggered the release happened to carry. The current firing's own data is discarded; only the fact that something is now due survives from it.
Example
A run is active, one firing is buffered, and a second firing arrives before the first is released:
{"n": 1}Once the blocking run ends, the release carries the buffered firing's own data, not the payload of whatever firing caused the release:
{"n": "buffered"}The firing that triggered the release, which had carried {"n": "current"},
contributes nothing but the fact that it was time to check again.