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

Relationships

#2580 workflow run log output: a forEach step with a templated name labels its iterations with the raw expression, e.g. deploy-${{ self.env }}[0]

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

Summary

In log mode, workflow run labels each forEach iteration's lines <template name>[<index>]. When the forEach step's name is itself an expression (a pattern the swamp-uat vary-mixed and vary-cel-data-access fixtures use), the label is the unevaluated template, for example deploy-${{ self.env }}[0]. The expanded step name (deploy-dev) is known, and since swamp-club#2538 it already appears in the body of skipped lines. The label column is widened to the raw expression, so every other line of the run is indented to match.

JSON output is not affected: step records use the expanded names.

Steps to reproduce

jobs:
  - name: main
    steps:
      - name: gate
        task: { type: model_method, modelType: command/shell, modelName: label-gate, methodName: execute, inputs: { run: 'true' } }
      - name: 'deploy-${{ self.env }}'
        dependsOn: [{ step: gate, condition: { type: succeeded } }]
        forEach: { item: env, in: '${{ ["dev","prod"] }}' }
        task: { type: model_method, modelType: command/shell, modelName: label-deploy, methodName: execute, inputs: { run: 'true' } }
      - name: 'rollback-${{ self.env }}'
        dependsOn: [{ step: gate, condition: { type: failed } }]
        forEach: { item: env, in: '${{ ["dev","prod"] }}' }
        task: { type: model_method, modelType: command/shell, modelName: label-rb, methodName: execute, inputs: { run: 'true' } }
      - name: 'warm-${{ self.region }}'
        guard: '${{ true }}'
        forEach: { item: region, in: '${{ ["us-east-1"] }}' }
        task: { type: model_method, modelType: command/shell, modelName: label-warm, methodName: execute, inputs: { run: 'true' } }

Observed (swamp source at ca3aa618, main)

warm-${{ self.region }}[0] │ skipped warm-us-east-1 (guarded) · guard: true
   main │ step gate · label-gate · execute · start 14:19:08 UTC
   main │ done gate in 37ms · 14:19:08 UTC
rollback-${{ self.env }}[0] │ skipped rollback-dev (dependency)
rollback-${{ self.env }}[1] │ skipped rollback-prod (dependency)
 deploy-${{ self.env }}[0] │ step deploy-dev · label-deploy · execute · start 14:19:08 UTC
 deploy-${{ self.env }}[0] │ done deploy-dev in 25ms · 14:19:08 UTC
                      main │ completed in 1.3s · 14:19:09 UTC

Iterations that run, iterations skipped by dependsOn, and guard-skipped iterations all get the raw label. It predates #2538 and #2537 (seen on 20260925.163149.0-sha.bea803c9 for running iterations).

Expected

A label a reader can match to the step, such as the expanded name (deploy-dev), or the template with its index only when the template name has no expression. A forEach step with a plain name (rollback-each[0]) keeps its current label.

Cause

WorkflowRunRenderer.getDisplayName() in src/presentation/renderers/workflow_run.ts builds the label as ${forEachTemplate}[${forEachIndex}]. runJob() in src/domain/workflows/execution_service.ts sets forEachTemplate to the key of the forEach expansion map, which is the step name as written, expression included. No renderer test covers a templated forEach name.

Found while end-to-end testing swamp-club#2537.

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

Shipped

9/29/2026, 3:19:26 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
hammz assigned hammz9/29/2026, 2:34:11 PM

Sign in to post a ripple.