Relationships
#1727 Concurrent suspended workflow runs can't be disambiguated from 'approvals'; fresh runs should supersede matching-input suspended runs
Opened by dmc · 8/19/2026· Shipped 8/28/2026
Environment
swamp 20260725.011748.0-sha.bb2cb3ef (macOS). Manual-approval workflow (type: manual_approval step) with a downstream model_method step.
Problem
Two related gaps make manual-approval workflows hard to operate once more than one run of the same workflow is suspended at the same gate. I hit both while driving a real approval workflow and ended up with 7 indistinguishable suspended runs.
Problem 1 — approvals doesn't surface run identity, and its suggested commands are ambiguous
swamp workflow approvals(human output) prints neitherrunIdnorsuspendedAt— both exist only in--json— and emits an identicalswamp workflow approve "<wf>" "<step>"/rejectline for every pending run.- With more than one run suspended,
approve/rejectwithout--runfail withMultiple suspended runs found…. That error does helpfully list the run ids — butapprovals, the command whose whole job is to show pending approvals, does not. - Net effect: from the tool's own recommended output you cannot tell which run you would be approving, and there is no hint that
--runis needed until you try. Scripts that doreject "<wf>" "<step>"in a loop silently no-op once ambiguity sets in, which is how the runs piled up.
Problem 2 — a fresh run doesn't supersede prior suspended runs of the same workflow
- Each
swamp workflow runof an already-suspended workflow adds another suspended run indefinitely. This is what produced the ambiguity in Problem 1.
Proposed solution
Make
approvalsself-sufficient for disambiguation. PrintrunId+suspendedAt(and ideally a one-line inputs digest) per pending run, and echo the ready-to-run--run <id>approve/reject command for each — so the listing command alone tells you exactly what to type.Supersede matching-input suspended runs on a fresh run. On a new
swamp workflow run, cancel/supersede prior suspended runs of the same workflow whose resolved inputs match — same inputs = same intent, so the newer run is authoritative. This is the key scoping constraint: runs with different inputs must be left alone. Example: anapply-sieverun withmailboxRole=archivemust not cancel a suspendedmailboxRole=inboxrun — they are independent intents. Could be default-on with an opt-out, or an explicit--supersedeflag.
Alternatives considered
- Manually
reject --run <id>each stale run — works, but only after discovering the ids via the error message or--json, which is the friction this request is about. - Per-run output naming (
${{ run.id }}) to avoid data collisions between concurrent runs — solves data safety but not the approval-disambiguation or run-pileup UX.
Reproduction
- Define a workflow with a
manual_approvalstep. swamp workflow run <wf> …twice (do not approve).swamp workflow approvals→ two identical entries, no run ids shown.swamp workflow approve <wf> <step>→Multiple suspended runs found.
Shipped
Click a lifecycle step above to view its details.