Relationships
#2895 workflow cancel and supersede of a suspended run leave its jobs running in the cancelled record
Opened by hammz · 10/1/2026· Shipped 10/1/2026
Description
Cancelling a suspended workflow run with swamp workflow cancel, or superseding it with a new swamp workflow run with matching inputs, marks the run cancelled but leaves its jobs running and their gates waiting_approval in the cancelled record. The same happens when workflow cancel clears a run whose owning process already died. swamp workflow history get keeps showing those jobs as in progress (◐) for good.
This is the record symptom swamp-club#2597 fixes for a cancelled resume, reached through a path that fix does not touch. cancelLocalRun (src/cli/commands/workflow_cancel.ts) and the supersede path call WorkflowRun.cancel() (src/domain/workflows/workflow_run.ts), which changes only the run status and never settles the run's jobs or steps. Related: swamp-club#2867 (settling a parent's suspended nested child runs on cancel/reject/supersede); this issue is about the run's own jobs.
Steps to reproduce
Repro workflow (workflows/workflow-e2e-wf.yaml; each step appends to a log so real executions can be counted):
id: <uuid>
name: e2e-wf
version: 1
concurrency: 1
jobs:
- name: a-side
steps:
- name: gate2
task: { type: manual_approval, prompt: "gate2?" }
- name: s
dependsOn: [{ step: gate2, condition: { type: succeeded } }]
task: { type: model_method, modelType: command/shell, modelName: m-s, methodName: execute,
inputs: { run: "echo s >> /tmp/exec.log; sleep 3" } }
- name: main
steps:
- name: gate
task: { type: manual_approval, prompt: "gate?" }
- name: post
dependsOn: [{ step: gate, condition: { type: succeeded } }]
task: { type: model_method, modelType: command/shell, modelName: m-post, methodName: execute,
inputs: { run: "echo post >> /tmp/exec.log" } }
- name: teardown
dependsOn: [{ job: main, condition: { type: always } }]
steps:
- name: t
task: { type: model_method, modelType: command/shell, modelName: m-t, methodName: execute,
inputs: { run: "echo t >> /tmp/exec.log" } }swamp workflow run e2e-wf(suspends witha-sideandmainbothrunning, gateswaiting_approval).swamp workflow approve e2e-wf gate --run <id>.swamp workflow cancel e2e-wf --run <id>.- Inspect the record (
swamp workflow history get <id> --json, or.swamp/workflow-runs/<wfId>/workflow-run-<id>.yaml).
Supersede variant: instead of steps 2–3, run swamp workflow run e2e-wf a second time; the first run is cancelled with cancel_reason: Superseded by new run with matching inputs.
Actual
run status=cancelled cancel_reason=Cancelled by user
job a-side: running | gate2:waiting_approval s:pending
job main: running | gate:succeeded post:pending
job teardown: pending | t:pendingThe supersede variant gives the same shape, with both gates waiting_approval.
Expected
A cancelled run has no job or step left running or waiting_approval. Its unfinished jobs and steps are settled the way an aborted run settles them: pending steps cancelled or skipped with settledByAbort, and the jobs ended.
Environment: Linux x86_64. Reproduced with release 20260930.225800.0-sha.1a9b497f and with the swamp-club#2597 branch (e337ad45), so it is not a regression from that fix. Found while end-to-end validating swamp-club#2597.
Shipped
Click a lifecycle step above to view its details.
Sign in to post a ripple.