Skip to main content
← Back to list
01Issue
BugOpenExtensionsPublic
Assigneesstack72

Relationships

#1678 workflow-succeeded selects the globally-latest run, so siblings block each other under parallel use

Opened by david_workingmemory · 8/16/2026

Reported against @swamp/software-factory@2026.06.24.1, swamp 20260814.003027.0, macOS.

Summary

workflowSucceeded picks the run to verify by matching on workflow name against the globally-latest run record. In a factory serving several work items concurrently — which the model explicitly supports, since every method takes workItem — all of them share one ci-checks-style workflow, and therefore one pointer. Whichever item runs the workflow last owns it, and every other item at that stage is blocked until it re-dispatches.

requireStepOutputs mitigates the inner half of this and works well: I use it to bind a run to a specific work item, and a sibling can no longer satisfy my gate. But it cannot help with the outer selection — the loop has already chosen the wrong run before required outputs are consulted.

The code anticipates the problem it does not fully solve. gates.ts around the requireStepOutputs block reads:

// Scope to this run own data: in parallel use, sibling runs share
// the workflow, and an unscoped match would accept their outputs.

That is exactly right about parallel use, but the run selection above it is still unscoped.

Reproduction

Two work items, A and B, both at a mode: workflow stage whose transition gates on workflow-succeeded for workflow W.

  1. Run W for A. A green, gate satisfied.
  2. Run W for B. Now B green.
  3. Re-check A. A is blocked, citing B run id.

Observed, with requireStepOutputs: ["evidence-ci-verified"] configured:

verified run 8238c24a-... did not write required output evidence-ci-verified into run wm-delivery

A red sibling is worse — it blocks everyone with a status failure that names a run belonging to a different item:

latest run of workflow ci-checks (b492138a-...) has status failed with 1 failed step(s)

Two items can flip-flop indefinitely, each invalidating the other by running the workflow.

Impact

Fails closed, so it is a false negative rather than a false positive — I could not construct an interleaving that produces a false pass, because the runId pin plus the ranAt < enteredAt freshness check block stale and foreign runs. Safety is intact.

Usability is not. Each collision costs a full CI re-watch, triggered by an event outside the blocked item control. At one or two items in flight this is friction; at four or more it is a starvation pattern — and this gate sits precisely where parallelism lives, upstream of a serialized deploy.

Suggested direction

Select the run per work item rather than globally: search run records for the latest run that wrote this item required step outputs, instead of taking the globally-latest record and then testing it. That reuses machinery already present — physicalStepOutput() already derives the per-item name — and would make requireStepOutputs scope selection as well as verification.

A narrower alternative, if that is too large: when requireStepOutputs is configured and the latest run fails the check, continue the loop to older run records rather than returning immediately, so an items own earlier verified run can still satisfy it within the freshness window.

Not a blocker for me

I have shipped around it and the gate is correct as documented. Filing because the comment in gates.ts shows the parallel case is intended to be handled, and this is the half that is not.

Upstream repository: https://github.com/swamp-club/swamp-extensions

Environment

  • Extension: @swamp/software-factory@2026.06.24.1
  • swamp: 20260815.023608.0-sha.a648c130
  • OS: darwin (aarch64)
  • Deno: 2.8.3
  • Shell: /bin/zsh
02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED

Open

8/16/2026, 11:45:19 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack728/17/2026, 2:45:25 PM

Sign in to post a ripple.