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

Relationships

#2713 forEach over a direct step with a shared modelName runs every iteration with one iteration's globalArgs on first creation

Opened by hammz · 9/29/2026· Shipped 9/29/2026

Summary

A forEach step that runs a model type directly with a fixed modelName and per-iteration globalArgs runs every iteration with one iteration's global arguments, but only on the run that first creates the auto-definition. No error or warning is raised. Method inputs are unaffected, and later runs are correct.

Reproduction (swamp 20260929.154804.0-sha.80c0526f)

Extension model @repro/echo: one global argument message, and a method record(label) that writes a resource named label holding { label, message }.

- name: record-${{ self.item }}
  forEach:
    item: item
    in: ${{ ['a', 'b', 'c', 'd'] }}
  task:
    type: model_method
    modelType: "@repro/echo"
    modelName: echo-shared
    methodName: record
    globalArgs:
      message: ${{ self.item }}
    inputs:
      label: ${{ self.item }}

First swamp workflow run (the definition does not exist yet). The run succeeds, and swamp data get echo-shared <label> shows:

a -> a
b -> a
c -> a
d -> a

The same happened on 3 of 3 fresh modelNames. The winning value varies (a, b, d), so it is whichever iteration created the definition. The second run, against the existing definition, is correct (a -> a, b -> b, ...). A templated modelName: echo-${{ self.item }} is correct on every run.

Expected

Each iteration runs with its own globalArgs, as it does on later runs. Failing that, swamp should reject or warn about per-iteration globalArgs on a shared modelName.

Likely cause

In resolveOrCreateDefinition (src/libswamp/models/direct_execution.ts, the locked re-check around line 448), iterations that lose the auto-creation race adopt the winner's definition without reconciling global arguments. The comment there reads "concurrent auto-creators share the same input line, so args are identical", which does not hold for forEach iterations whose globalArgs depend on self.<item>. The existing-definition path above it does reconcile (setGlobalArgument / removeGlobalArgument), which explains why later runs are correct.

Found while working on #2492 (unrelated to it).

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

Shipped

9/29/2026, 7:04:39 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
hammz assigned hammz9/29/2026, 5:59:25 PM

Sign in to post a ripple.