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

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" } }
  1. swamp workflow run e2e-wf (suspends with a-side and main both running, gates waiting_approval).
  2. swamp workflow approve e2e-wf gate --run <id>.
  3. swamp workflow cancel e2e-wf --run <id>.
  4. 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:pending

The 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.

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

Shipped

10/1/2026, 8:31:30 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
hammz assigned hammz10/1/2026, 4:32:32 PM

Sign in to post a ripple.