Skip to main content
← Back to list
01Issue
BugShippedSwamp CLI
Assigneesstack72

#411 workflow direct-execution inputs.* persist as globalArguments on auto-definitions and freeze on first run

Opened by jentz · 5/22/2026· Shipped 5/22/2026

Summary

When a workflow uses direct execution (task.type: model_method with modelType + modelName + inputs.{...} on the step), the first workflow run resolves inputs.* via CEL and writes the resolved values to .swamp/auto-definitions/<modelType>/<modelId>.yaml as globalArguments. Subsequent runs of the same workflow ignore the workflow's current resolved inputs.* and re-use whatever was persisted on the first run.

The workflow-input → model-globalArg flow is effectively one-shot. Parameterized workflows that vary an inputs.* per run silently use the first run's values forever.

Environment

  • swamp 20260521.221703.0-sha.3f2b75f8
  • macOS arm64

Reproduction

  1. Define a model extension with a globalArguments zod schema that includes (e.g.) a string selector:

    const GlobalArgsSchema = z.object({
      selector: z.string().default("true"),
    });
  2. Define a workflow with a numeric input and direct-execute the model with a CEL-built selector:

    name: my-workflow
    inputs:
      properties:
        memberCount:
          type: integer
      required: [memberCount]
    jobs:
      - name: inventory
        steps:
          - name: list
            task:
              type: model_method
              modelType: "@me/my-model"
              modelName: my-inv
              methodName: list_things
              inputs:
                selector: ${{ 'count == ' + string(inputs.memberCount) }}
  3. First run:

    swamp workflow run my-workflow --input '{"memberCount": 2}'

    Then inspect:

    cat .swamp/auto-definitions/@me/my-model/<modelId>.yaml
    # globalArguments:
    #   selector: count == 2
  4. Run again with a different memberCount:

    swamp workflow run my-workflow --input '{"memberCount": 3}'
  5. Re-inspect:

    cat .swamp/auto-definitions/@me/my-model/<modelId>.yaml
    # globalArguments:
    #   selector: count == 2    ← unchanged from first run
  6. Confirm the workflow CEL resolves the new value correctly via:

    swamp workflow evaluate my-workflow --input '{"memberCount": 3}'

    The evaluated workflow shows selector: count == 3 — but the actual run uses the persisted count == 2.

Expected

Workflow inputs.* values flow through to the model's globalArguments on every run, reflecting that run's actual inputs. Either the persisted auto-definition refreshes on each invocation, or workflow inputs.* override the persisted value at invocation time without mutating the file.

Actual

The first run's resolved inputs.* values are persisted permanently to the auto-definition. Subsequent runs use those persisted values regardless of the workflow's current input.

Side observations

  • swamp model get <modelName> --json returns globalArgs: null for a model created via direct-execution auto-definition, even though the auto-definition file plainly has populated globalArguments. The read path doesn't surface the persisted value, but the runtime obviously consults it from somewhere.
  • The bug is deterministic in 5/5 fresh attempts. Direct-execution + workflow inputs is the only path that triggers it; pre-created models via swamp model create --global-arg ... honour each invocation's actual globalArgs, because there's no per-workflow auto-definition mechanism interposing.

Workarounds

  • rm -rf .swamp/auto-definitions/<modelType>/ between runs — forces a fresh modelId each time and re-persists with the new inputs. Loses any data continuity tied to the original modelId.
  • Pre-create one model definition per input shape via swamp model create <type> <name> --global-arg ... and reference each by name. Sidesteps the auto-definition mechanism entirely but means N workflow definitions (or N model definitions, one per shape) instead of one parameterized workflow.

Extension code in PR 13

Impact

Blocks the documented parameterized-workflow pattern. Users who write inputs.foo: ${{ 'expr ' + string(inputs.bar) }} and vary bar per invocation will silently see only the first run's value used. The mismatch is hard to spot because swamp workflow evaluate substitutes correctly and the per-run YAML records the workflow's resolved inputs — only the auto-definition's frozen globalArguments reveals the divergence.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 2 MOREREVIEW+ 3 MOREPR_MERGED+ 1 MORECONTRIBUTOR_NOTIFIED

Shipped

5/22/2026, 5:45:05 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack725/22/2026, 3:32:04 PM
Editable. Press Enter to edit.

stack72 commented 5/22/2026, 5:45:11 PM

Thanks @jentz for reporting this! The fix has been merged and a release is on its way. We appreciate your contribution to swamp.

Sign in to post a ripple.