WORKFLOWS
A workflow is a YAML file that orchestrates the execution of model methods and
other workflows. Workflows live in the workflows/ directory within a Swamp
repository.
File Structure
workflows/
workflow-e182b5c7-41e7-4c9a-a9f8-455b7bb5ce12.yaml
workflow-d14de37a-b931-4363-97e9-fe9ec9637b60.yamlThe filename is workflow-{uuid}.yaml.
Top-Level Fields
id: d14de37a-b931-4363-97e9-fe9ec9637b60
name: deploy-pipeline
description: A multi-stage deployment pipeline
trigger:
schedule: "0 3 * * *"
tags:
team: platform
inputs:
type: object
properties:
region:
type: string
default: us-east-1
required:
- region
jobs:
- name: build
steps:
- name: compile
task:
type: model_method
modelIdOrName: builder
methodName: run
version: 1
reports:
require:
- summary
skip:
- debug-reportid
Unique identifier for the workflow.
| Property | Value |
|---|---|
| Type | string (UUID v4) |
| Required | Yes |
| Default | Auto-generated on create |
| Format | xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
name
Human-readable name for the workflow.
| Property | Value |
|---|---|
| Type | string |
| Required | Yes |
| Default | None |
Constraints:
- Minimum 1 character.
- Must be unique within the repository.
- Must not contain
..,\, or null bytes (path traversal protection). /is only allowed in scoped names matching@[a-z0-9_-]+/[a-z0-9_-]+(\/[a-z0-9_-]+)*(e.g.,@team/deploy-pipeline).
description
Human-readable description of the workflow's purpose.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | None |
trigger
Scheduling configuration for automatic execution.
| Property | Value |
|---|---|
| Type | object |
| Required | No |
| Default | None |
trigger.schedule
Cron expression for recurring execution.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Format | Standard cron (5 fields): minute hour dom month dow |
Validated at parse time using the Croner library. Invalid expressions cause a schema validation error.
trigger:
schedule: "0 3 * * *" # Daily at 3:00 AMtrigger:
schedule: "*/15 * * * *" # Every 15 minutestrigger:
schedule: "0 3,12 * * *" # 3:00 AM and 12:00 PM dailytrigger.inputs
Baseline input values supplied to trigger-fired runs. Trigger-fired runs
(scheduled and webhook) have no --input flag, so trigger.inputs provides the
values at fire time.
| Property | Value |
|---|---|
| Type | Record<string, unknown> |
| Required | No |
| Default | None |
This is a free values map — the runtime values to inject — distinct from the
workflow's inputs block, which is the JSON Schema describing
allowed inputs.
trigger:
schedule: "0 3 * * *"
inputs:
projectId: "a6b254a2-0b57-4d0f-bf8b-fef767ab119e"Values merge as caller inputs > trigger.inputs > schema defaults — the same
merge as --input on swamp workflow run. A scheduled run has no caller, so
trigger.inputs is the baseline. A webhook run extracts no inputs from its
payload (the body is used only for signature verification), so it also takes its
baseline from trigger.inputs. The merged values pass through the same
coercion, default application, and validation as every other run.
trigger.inputs lets a workflow declare required inputs rather than relying
on the schema default, which would apply to every caller. It applies only to
trigger-fired runs; a manual swamp workflow run supplies inputs explicitly and
is unaffected. See Webhook Triggers for webhook-fired runs.
tags
Key-value labels attached to the workflow.
| Property | Value |
|---|---|
| Type | Record<string, string> |
| Required | No |
| Default | {} |
tags:
team: platform
env: productioninputs
JSON Schema definition for structured inputs provided at runtime. Uses the same
InputsSchema format as
model definition inputs.
| Property | Value |
|---|---|
| Type | InputsSchema |
| Required | No |
| Default | None |
inputs:
type: object
properties:
environments:
type: array
description: Target environments
region:
type: string
default: us-east-1
required:
- environmentsInput values are available in CEL expressions as inputs.* within step task
inputs and forEach expressions.
jobs
Ordered list of jobs to execute.
| Property | Value |
|---|---|
| Type | Job[] |
| Required | Yes |
| Minimum | 1 |
See Job below.
version
Workflow version number.
| Property | Value |
|---|---|
| Type | integer |
| Required | No |
| Default | 1 |
Must be a positive integer.
concurrency
Maximum number of jobs that execute in parallel at each dependency level.
| Property | Value |
|---|---|
| Type | integer |
| Required | No |
| Default | None |
Must be a non-negative integer. When unset or 0, parallelism is unbounded.
Jobs and steps can override this value. See
Concurrency Resolution Order.
concurrency: 4reports
Post-execution report selection.
| Property | Value |
|---|---|
| Type | ReportSelection |
| Required | No |
| Default | None |
Uses the same format as model definition reports.
reports:
require:
- summary
- name: deployment-report
methods:
- apply
skip:
- debug-reportRequired reports must resolve or the workflow run fails. The resolution rules are the same as for model definition reports — an unresolvable name logs a warning, writes an error artifact, and counts as a report failure.
swamp workflow resume executes workflow-scope and step-scope reports,
including reports.require. Previously, resumed workflows skipped all reports.
Webhook Triggers
A workflow can be triggered by an external HTTP webhook in addition to a
trigger.schedule cron. Webhook endpoints are registered on
the worker, not in the workflow file, with the --webhook flag of
swamp serve:
swamp serve --webhook /hooks/github:my-workflow:$WEBHOOK_SECRETThe flag value is <route>:<workflow>:<secret>. The route must start with /;
the secret is everything after the second colon, so it may itself contain
colons.
Request contract
A registered route accepts POST requests. Each request must carry an
X-Hub-Signature-256 header whose value is sha256=<hex>, where <hex> is the
HMAC-SHA256 of the raw request body keyed by the shared secret (the GitHub
webhook scheme).
| Status | Condition |
|---|---|
200 |
Valid signature — the run is queued: { "status": "queued" } |
401 |
Missing or invalid X-Hub-Signature-256 signature |
413 |
Request body exceeds 10 MB |
503 |
The run queue is full (maximum 100 queued runs) |
A queued run executes through the same path as scheduled and CLI runs.
Inputs
The request body is used only for signature verification — no inputs are
extracted from the payload. A webhook-fired run takes its baseline input values
from trigger.inputs, exactly like a scheduled run.
Job
A job groups related steps within a workflow. Jobs with no dependencies on each other execute in parallel.
jobs:
- name: build
description: Compile and test
steps:
- name: compile
task:
type: model_method
modelIdOrName: builder
methodName: run
dependsOn: []
weight: 0name
Job name, unique within the workflow.
| Property | Value |
|---|---|
| Type | string |
| Required | Yes |
| Minimum | 1 char |
description
Human-readable description of the job.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | None |
steps
Ordered list of steps to execute within the job.
| Property | Value |
|---|---|
| Type | Step[] |
| Required | Yes |
| Minimum | 1 |
See Step below.
dependsOn
Dependencies on other jobs with trigger conditions.
| Property | Value |
|---|---|
| Type | JobDependency[] |
| Required | No |
| Default | [] |
Each element specifies which job to depend on and under what condition:
dependsOn:
- job: build
condition:
type: succeededSee Trigger Conditions for the full condition schema.
weight
Numeric weight for deterministic ordering of jobs at the same dependency level.
| Property | Value |
|---|---|
| Type | number |
| Required | No |
| Default | 0 |
Lower weights execute first. Jobs with the same weight and dependency level are ordered alphabetically by name.
concurrency
Maximum number of steps that execute in parallel at each dependency level within
this job. Overrides the workflow-level concurrency.
| Property | Value |
|---|---|
| Type | integer |
| Required | No |
| Default | None |
Must be a non-negative integer. When unset or 0, falls back to the
workflow-level value. See
Concurrency Resolution Order.
Step
A step is a single unit of work within a job. Steps with no dependencies on each other execute in parallel.
steps:
- name: deploy-env
description: Deploy to environment
task:
type: model_method
modelIdOrName: deployer
methodName: apply
forEach:
item: env
in: "${{ inputs.environments }}"
dependsOn:
- step: compile
condition:
type: succeeded
weight: 0
dataOutputOverrides:
- specName: result
lifetime: 30d
garbageCollection: 5
tags:
stage: deploy
vary:
- environment
allowFailure: falsename
Step name, unique within the job.
| Property | Value |
|---|---|
| Type | string |
| Required | Yes |
| Minimum | 1 char |
When forEach is used and the step name contains ${{ }} expressions, the name
is evaluated with the iteration context to produce unique names per iteration.
description
Human-readable description of the step.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | None |
task
The work to execute. See Step Task below.
| Property | Value |
|---|---|
| Type | StepTask |
| Required | Yes |
forEach
Iteration configuration. When set, the step expands into one execution per element.
| Property | Value |
|---|---|
| Type | ForEach |
| Required | No |
| Default | None |
See ForEach below.
dependsOn
Dependencies on other steps within the same job.
| Property | Value |
|---|---|
| Type | StepDependency[] |
| Required | No |
| Default | [] |
dependsOn:
- step: compile
condition:
type: succeededSee Trigger Conditions for the full condition schema.
weight
Numeric weight for deterministic ordering of steps at the same dependency level.
| Property | Value |
|---|---|
| Type | number |
| Required | No |
| Default | 0 |
Lower weights execute first. Steps with the same weight and dependency level are ordered alphabetically by name.
dataOutputOverrides
Overrides for data output specifications produced by the step's task.
| Property | Value |
|---|---|
| Type | DataOutputOverride[] |
| Required | No |
| Default | None |
See DataOutputOverride below.
allowFailure
When true, a step failure does not fail the job. Subsequent steps with
succeeded conditions against this step will not trigger, but completed and
always conditions will.
| Property | Value |
|---|---|
| Type | boolean |
| Required | No |
| Default | false |
concurrency
Maximum number of steps that execute in parallel at this step's dependency level
within the job. Overrides job-level and workflow-level concurrency.
| Property | Value |
|---|---|
| Type | integer |
| Required | No |
| Default | None |
Must be a non-negative integer. When unset or 0, falls back to the job-level
value. See Concurrency Resolution Order.
target
Dispatch this step to a specific remote worker by name or instance UUID. See Workflow Placement for scheduling behavior and interaction with non-placed steps.
| Property | Value |
|---|---|
| Type | string |
| Required | No |
| Default | None |
Mutually exclusive with labels.
labels
Dispatch this step to any connected remote worker whose labels are a superset of this selector. See Workflow Placement for scheduling behavior and interaction with non-placed steps.
| Property | Value |
|---|---|
| Type | Record<string, string> |
| Required | No |
| Default | None |
Mutually exclusive with target.
Step Task
A step task defines the work a step executes. Three task types are available.
Model Method (model_method)
Invokes a method on a model definition. Two mutually exclusive field sets identify the target model.
modelIdOrName variant
Targets an existing definition by name or UUID.
task:
type: model_method
modelIdOrName: deployer
methodName: apply
inputs:
target: "${{ inputs.region }}"| Field | Type | Required | Description |
|---|---|---|---|
type |
"model_method" |
Yes | Task type discriminator |
modelIdOrName |
string | Yes | Model definition name or UUID |
methodName |
string | Yes | Method to invoke |
inputs |
Record<string, unknown> | string |
No | Input values for the method (record or expression) |
globalArgs |
Record<string, unknown> | string |
No | Global arguments for the model definition |
modelType + modelName variant
Targets a model type directly. When no definition with the given name exists,
one is created automatically in .swamp/auto-definitions/. inputs values are
split between global arguments and method arguments using the type's schemas —
method arguments take precedence on ambiguous keys.
task:
type: model_method
modelType: "@swamp/aws/ec2/vpc"
modelName: my-vpc
methodName: create
inputs:
region: us-east-1
cidr: "10.0.0.0/16"| Field | Type | Required | Description |
|---|---|---|---|
type |
"model_method" |
Yes | Task type discriminator |
modelType |
string | Yes | Model type identifier |
modelName |
string | Yes | Name for the created definition |
methodName |
string | Yes | Method to invoke |
inputs |
Record<string, unknown> | string |
No | Input values for the method (record or expression) |
globalArgs |
Record<string, unknown> | string |
No | Global arguments for the model definition |
modelName supports CEL template expressions in forEach steps:
- name: scan-${{ self.host }}
forEach: { item: host, in: "${{ inputs.hosts }}" }
task:
type: model_method
modelType: "@swamp/cve/dirtyfrag"
modelName: fleet-scanner
methodName: scanFleet
inputs:
host: ${{ self.host }}Variant constraints
A step must contain exactly one of these field sets. Validation rejects:
- Both
modelIdOrNameandmodelTypepresent modelTypewithoutmodelName- Neither
modelIdOrNamenormodelTypepresent
inputs
inputs accepts two forms. The record form is a map of key-value pairs
where individual values may contain ${{ }} CEL expressions:
inputs:
target: "${{ inputs.region }}"
retries: 3The expression form is a single ${{ }} CEL expression that evaluates to a
record at runtime:
inputs: "${{ self.item.implementation.inputs }}"The expression must evaluate to a map — the runtime passes the resolved key-value pairs as input values. The two forms are mutually exclusive for a given task.
Both forms are supported on model_method and workflow task types.
globalArgs
globalArgs sets global arguments on the target model definition. It accepts
the same two forms as inputs — a literal record or a single
expression. Available on model_method tasks only.
task:
type: model_method
modelIdOrName: scanner
methodName: run
inputs:
target: "${{ self.host }}"
globalArgs:
region: "${{ inputs.region }}"When using the modelType + modelName variant without globalArgs, keys in
inputs are split between global arguments and method arguments using the
type's schemas. Setting globalArgs explicitly separates the two.
forEach with whole-field expressions
The expression form is particularly useful in forEach steps where each
iteration item carries its own input record. Instead of mapping individual keys,
pass the record through wholesale:
steps:
- name: run-${{ self.task.name }}
forEach:
item: task
in: "${{ inputs.tasks }}"
task:
type: model_method
modelType: "${{ self.task.modelType }}"
modelName: "${{ self.task.name }}"
methodName: run
inputs: "${{ self.task.inputs }}"
globalArgs: "${{ self.task.globalArgs }}"Given workflow inputs like:
tasks:
- name: scan-us
modelType: "@swamp/scanner"
inputs: { region: us-east-1, depth: full }
globalArgs: { pool: 4 }
- name: scan-eu
modelType: "@swamp/scanner"
inputs: { region: eu-west-1, depth: quick }
globalArgs: { pool: 2 }Each iteration expands self.task to the current element, resolving inputs
and globalArgs to the nested records without enumerating every key.
Workflow (workflow)
Invokes another workflow as a nested execution.
task:
type: workflow
workflowIdOrName: notification-workflow
inputs:
channel: "#deployments"| Field | Type | Required | Description |
|---|---|---|---|
type |
"workflow" |
Yes | Task type discriminator |
workflowIdOrName |
string | Yes | Workflow name or UUID |
inputs |
Record<string, unknown> | string |
No | Input values passed to the workflow (record or expression) |
Nested workflows have a maximum depth of 10. Cyclic references (workflow A invoking workflow B which invokes workflow A) are detected and rejected.
Manual Approval (manual_approval)
Suspends the workflow run and waits for an out-of-band approval decision before downstream steps execute.
task:
type: manual_approval
prompt: "Staging verified. Approve production rollout?"
timeout: 3600| Field | Type | Required | Description |
|---|---|---|---|
type |
"manual_approval" |
Yes | Task type discriminator |
prompt |
string | Yes | Message shown to the approver. Must be non-empty. |
timeout |
number | No | Approval window in seconds. Must be greater than 0. |
When timeout is set, an approval submitted after the window has elapsed is
rejected with an Approval timed out error and the gate remains unapproved. An
expired gate also no longer appears in swamp workflow approvals. When
timeout is omitted, the gate waits indefinitely.
The suspend, approve, reject, and resume lifecycle is described in Manual Approval Lifecycle. For the step-by-step procedure, see the how-to guide Gate a Workflow with Manual Approval.
ForEach
Expands a step into multiple executions, one per element in the evaluated collection.
forEach:
item: env
in: "${{ inputs.environments }}"| Field | Type | Required | Description |
|---|---|---|---|
item |
string | Yes | Variable name bound to each element |
in |
string | Yes | CEL expression evaluating to a list or map |
When iterating over a list, self.{item} is set to each element.
When iterating over a map, self.{item} is set to an object with key and
value fields for each entry.
Expanded steps inherit the original step's dependencies. If the step name
contains ${{ }} expressions, they are evaluated with the iteration context to
produce unique step names. Otherwise, the iteration value or index is appended
as a suffix (e.g., deploy-env-staging, deploy-env-production).
The iteration variable (self.{item}) resolves in modelIdOrName,
methodName, step name, and task inputs — not just in inputs. This
enables forEach steps to target different model instances per iteration without
requiring a nested workflow:
steps:
- name: summary-${{ self.region }}
forEach:
item: region
in: "${{ inputs.regions }}"
task:
type: model_method
modelIdOrName: aws-alarms-${{ self.region }}
methodName: get_summary
inputs:
historyHours: 24Each iteration resolves self.region to the current element, producing steps
like summary-us-east-1 that target aws-alarms-us-east-1, summary-eu-west-1
that target aws-alarms-eu-west-1, and so on.
Trigger Conditions
Trigger conditions control when a job or step executes relative to its
dependencies. They are specified in the condition field of dependsOn
entries.
Leaf Conditions
| Type | Evaluates to true when |
|---|---|
always |
Always (unconditional) |
succeeded |
The dependency succeeded |
failed |
The dependency failed |
completed |
The dependency completed (succeeded or failed) |
skipped |
The dependency was skipped |
dependsOn:
- job: build
condition:
type: succeededComposite Conditions
Leaf conditions can be composed with boolean logic.
and — all child conditions must be true. Requires at least 2 conditions.
condition:
type: and
conditions:
- type: succeeded
- type: not
condition:
type: skippedor — any child condition must be true. Requires at least 2 conditions.
condition:
type: or
conditions:
- type: succeeded
- type: failednot — inverts a single condition.
condition:
type: not
condition:
type: failedComposite conditions nest recursively.
DataOutputOverride
Overrides data output specifications for data produced by a step's task.
| Field | Type | Required | Description |
|---|---|---|---|
specName |
string | Yes | Output spec name to override |
lifetime |
Lifetime |
No | Override data retention |
garbageCollection |
GarbageCollectionPolicy |
No | Override version retention |
tags |
Record<string, string> |
No | Additional tags to merge with output tags |
vary |
string[] |
No | Input key names to vary by (composite data names) |
Lifetime
How long data is retained.
| Value | Description |
|---|---|
| Duration string | 1h, 5m, 10d, 2w, 1mo, 10y |
ephemeral |
Deleted when the process ends |
infinite |
Never automatically deleted |
job |
Lives until the job completes |
workflow |
Lives until the workflow completes |
Duration format: {number}{unit} where unit is h (hours), m (minutes), d
(days), w (weeks), mo (months), or y (years). Zero durations (e.g., 0h)
are normalized to workflow.
GarbageCollectionPolicy
How many versions to retain.
| Value | Description |
|---|---|
| integer | Keep N most recent versions |
| Duration string | Keep versions created within the duration |
dataOutputOverrides:
- specName: result
lifetime: 30d
garbageCollection: 5
tags:
stage: deploy
vary:
- environmentCEL Expressions
String values in step task inputs, modelIdOrName, methodName, step name,
and forEach.in fields support CEL expressions using the ${{ }} wrapper.
task:
type: model_method
modelIdOrName: deployer
methodName: apply
inputs:
region: "${{ inputs.region }}"
artifact: "${{ model.builder.resource.result.result.attributes.path }}"
secret: "${{ vault.get('infra', 'deploy-key') }}"Context Variables
| Variable | Description |
|---|---|
self |
The current model definition: self.name, self.tags.*, self.globalArguments.* |
model |
All models: model.<name>.definition.*, model.<name>.resource.<spec>.<instance>.attributes.* |
inputs |
Workflow or model runtime inputs: inputs.<property> |
env |
Process environment variables: env.<VAR_NAME> |
vault |
Secrets: vault.get('<vault>', '<key>') |
data |
Versioned data: data.latest(...), data.version(...), data.findBySpec(...), data.query(...) |
file |
File contents: file.contents('<model>', '<spec>') |
When forEach is active, the iteration variable is available as self.{item}.
For example, with forEach: { item: "env", in: [...] }, each iteration sets
self.env to the current element.
After a step completes, its model's execution data and output data are available
to subsequent steps through the model context. For example,
model.builder.execution.status and
model.builder.resource.result.result.attributes.*.
See the CEL Expressions reference for the full expression language.
Execution Order
Jobs and steps are sorted topologically using Kahn's algorithm with weighted tie-breaking.
- Dependency level — nodes are grouped into levels based on the dependency graph. Level 0 has no dependencies, level 1 depends only on level 0, etc.
- Weight — within the same level, lower weights execute first.
- Name — within the same level and weight, names are sorted alphabetically for determinism.
Nodes at the same level with no mutual dependencies execute in parallel.
Cyclic dependencies are detected and produce a validation error with the cycle
path (e.g., job-a -> job-b -> job-a).
Manual Approval Lifecycle
A manual_approval step pauses execution until an approval decision is
recorded. While the gate is pending, the run holds at the gate and no downstream
step starts.
Run and step statuses
When a run reaches a manual_approval step, the workflow run status becomes
suspended and the gate step status becomes waiting_approval. Steps that ran
before the gate keep their terminal status (succeeded); steps after the gate
stay pending.
| Status | Applies to | Meaning |
|---|---|---|
suspended |
Run | The run is paused at a manual_approval step. |
waiting_approval |
Step | The gate is awaiting an approval decision. |
An approval transitions the gate step to succeeded. A rejection transitions
the gate step to failed and the run to failed; downstream steps remain
pending.
CLI Commands
Workflow management commands for the Swamp CLI.
swamp workflow create <name>
Create a new workflow file.
| Argument | Required | Description |
|---|---|---|
name |
Yes | Workflow name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
swamp workflow run <name>
Execute a workflow. The command blocks the calling process until the run reaches
a terminal state — succeeded, failed, or cancelled — or until the run
suspends at a manual_approval gate. There is no async or detached mode; the
exit code reflects the run outcome (0 for success, non-zero otherwise). To
abort a blocking run, press Ctrl+C (SIGINT) or send SIGTERM — see
Execution Cancellation.
| Argument | Required | Description |
|---|---|---|
name |
Yes | Workflow name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--input |
Set an input value (key=value). Repeatable. |
--input-file |
YAML file of input values. |
--stdin |
Read input values from standard input. |
--tag |
Tag the run (key=value). Repeatable. |
--skip-checks |
Skip all checks. |
--skip-check |
Skip a check by name. Repeatable. |
--skip-check-label |
Skip checks matching a label. Repeatable. |
--skip-reports |
Skip all reports. |
--skip-report |
Skip a report by name. Repeatable. |
--skip-report-label |
Skip reports matching a label. Repeatable. |
--report |
Run a specific report by name. Repeatable. |
--report-label |
Run reports matching a label. Repeatable. |
--timeout |
Maximum run duration. |
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |
--last-evaluated |
Reuse the previously evaluated workflow and definitions. See below. |
--last-evaluated
When set, swamp workflow run skips the full CEL evaluation pass and loads the
previously evaluated workflow from .swamp/workflows-evaluated/. This avoids
re-reading model definitions from disk and re-evaluating expressions that have
not changed.
Deferred data expressions (data.query(), data.findByTag(), data.latest(),
data.version(), data.listVersions(), data.findBySpec()) inside step
task.inputs are still resolved at step execution time. The engine builds a
lightweight expression context containing only the data and env namespaces,
which is sufficient because the only unevaluated expressions remaining are data
function calls that reference upstream step outputs.
swamp workflow get <name>
Show a workflow definition.
| Argument | Required | Description |
|---|---|---|
workflow_id_or_name |
Yes | Workflow ID or name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |
swamp workflow search [query]
Search workflows.
| Argument | Required | Description |
|---|---|---|
query |
No | Search query. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |
swamp workflow edit [name]
Open a workflow in $EDITOR.
| Argument | Required | Description |
|---|---|---|
workflow_id_or_name |
No | Workflow ID or name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
swamp workflow delete <name>
Delete a workflow and its run history.
| Argument | Required | Description |
|---|---|---|
workflow_id_or_name |
Yes | Workflow ID or name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
-f, --force |
Skip confirmation prompt. |
swamp workflow validate [name]
Validate a workflow's schema, dependencies, and method resolution.
| Argument | Required | Description |
|---|---|---|
workflow_id_or_name |
No | Workflow ID or name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
Omitting the name validates all workflows.
swamp workflow evaluate [name]
Test CEL expressions in a workflow without executing it.
| Argument | Required | Description |
|---|---|---|
workflow_id_or_name |
No | Workflow ID or name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--all |
Evaluate all workflows. |
--input |
Set an input value (key=value). Repeatable. |
--input-file |
YAML file of input values. |
swamp workflow schema get
Print the YAML schema for workflow files.
| Flag | Description |
|---|---|
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |
swamp workflow cancel [name]
Cancel a running workflow.
| Argument | Required | Description |
|---|---|---|
workflow_id_or_name |
No | Workflow ID or name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--all |
Cancel all running workflows. |
--run |
Target a specific run ID. |
--reason |
Cancellation reason for the audit trail. |
Approval decision record
When a gate is approved or rejected, an approvalDecision is written to the
gate's step run record.
| Field | Type | Description |
|---|---|---|
approved |
boolean | true for an approval, false for a rejection. |
reason |
string | The --reason text, when provided. Omitted otherwise. |
decidedBy |
string | The authenticated identity that made the decision. |
decidedAt |
string | ISO 8601 timestamp of the decision. |
Commands
| Command | Effect |
|---|---|
swamp workflow approvals |
Lists runs suspended at a gate and still within their approval window, with the workflow, step, and prompt. Runs whose timeout has elapsed are omitted — they can no longer be approved. |
swamp workflow approve <workflow> <step> |
Records an approval on the gate. Accepts --reason <text>. Does not run downstream steps. |
swamp workflow reject <workflow> <step> |
Records a rejection and marks the run failed. Accepts --reason <text>. |
swamp workflow resume <workflow> |
Resumes a suspended run after approval, skipping already-completed steps. Accepts override inputs via --input, --input-file, or --stdin — see Resume inputs. Accepts --timeout — see Resume timeout. |
All four commands accept --server and --token to target a remote
swamp serve instance.
approve records the decision but does not execute downstream work; resume
performs the execution. Resuming a run whose gate has not been approved fails
with an error naming the step to approve first. See
Understanding Workflow Suspension
for why approval and resume are separate operations.
Resume inputs
swamp workflow resume accepts override inputs through three flags:
| Flag | Source |
|---|---|
--input <key=value> |
A single key set on the command line. The value may be a scalar or JSON. Repeatable. |
--input-file <file> |
A YAML file of override inputs. |
--stdin |
Override inputs read as piped data from standard input. |
--input-file and --stdin cannot be combined. --input may accompany either.
Override inputs deep-merge over the original run inputs. On a key collision the resume value wins; keys not present in the original run are added. Nested objects merge key by key rather than replacing the whole object.
The run record records the names of the keys supplied at resume in a
resumeInputs list. It does not record their values. A secret minted during the
gate — such as a freshly issued auth key — is therefore usable at resume without
being written to the run record. See
Understanding Workflow Suspension
for why resume inputs are audited this way, and
Gate a Workflow with Manual Approval
for the auth-key task.
swamp workflow resume deploy-with-gate --input authKey=tskey-abc123Resume timeout
swamp workflow resume accepts --timeout to set a maximum run duration,
matching the same flag on swamp workflow run. The value is bare seconds (30,
1800) or a duration string (30s, 5m, 1h). When omitted, the resumed run
executes unbounded.
swamp workflow resume deploy-with-gate --timeout 5mExecution Cancellation
A running workflow can be cancelled via swamp workflow cancel. Cancellation
terminates in-flight steps cooperatively and marks the run as cancelled — a
terminal status alongside succeeded and failed.
cancelled status
| Status | Applies to | Meaning |
|---|---|---|
cancelled |
Run | The run was cancelled before completing. |
cancelled |
Step | The step was cancelled before completing. |
Steps that completed before the cancel signal keep their terminal status
(succeeded or failed). Steps that were running or pending at the time of
cancellation transition to cancelled.
Commands
| Command | Effect |
|---|---|
swamp workflow cancel <workflow> |
Cancel the latest running run of the named workflow. |
swamp workflow cancel <workflow> --run <run-id> |
Cancel a specific run by ID. |
swamp workflow cancel --all |
Cancel all running workflow runs in the repository. |
swamp workflow cancel <workflow> --reason <text> |
Cancel with an audit-trail reason recorded on the run. |
Signal-based cancellation
Pressing Ctrl+C (SIGINT) or sending SIGTERM during swamp workflow run cancels
the running workflow. The CLI intercepts the signal, sends a cancel request,
waits for in-flight steps to terminate cooperatively, and exits with a non-zero
status.
Examples
$ swamp workflow cancel my-workflow
$ swamp workflow cancel my-workflow --run 3f8a2b1c
$ swamp workflow cancel --all
$ swamp workflow cancel my-workflow --reason 'No longer needed'Concurrency Resolution Order
When steps within a job execute in parallel, the effective concurrency limit is resolved from the most specific level:
- Step
concurrency— if multiple steps at the same dependency level specifyconcurrency, the minimum value across those steps is used. - Job
concurrency - Workflow
concurrency - Unbounded
The first non-zero value at each level wins.
Job-level parallelism follows the same pattern: workflow concurrency is
applied across jobs at the same dependency level.
SWAMP_MAX_CONCURRENT_STEPS
Environment variable that sets a host-level ceiling on all concurrency values. When set to a positive integer, the effective concurrency at every level is capped to this value regardless of what the workflow, job, or step specifies. When unset or not a positive integer, no host-level cap is applied.
SWAMP_MAX_CONCURRENT_STEPS=4Validation
swamp workflow validate checks a workflow against its schema:
$ swamp workflow validate multi-stage
Validating: multi-stage
✓ Schema validation
✓ Unique job names
✓ Unique step names in job 'build'
✓ Unique step names in job 'deploy'
✓ Unique step names in job 'notify'
✓ Valid job dependency references
✓ Valid step dependency references in job 'build'
✓ Valid step dependency references in job 'deploy'
✓ Valid step dependency references in job 'notify'
✓ No cyclic job dependencies
✓ No cyclic step dependencies in job 'build'
✓ No cyclic step dependencies in job 'deploy'
✓ No cyclic step dependencies in job 'notify'
Summary: 13/13 validations passed
Result: PASSEDOmit the name to validate all workflows in the repository:
$ swamp workflow validate
Validating all workflows...
multi-stage
✓ Schema validation
...
Summary: 1/1 workflows passed
Overall: PASSEDValidation rules:
idmust be a valid UUID v4.namemust be at least 1 character, unique within the repository, with no path traversal sequences.versionmust be a positive integer.concurrencymust be a non-negative integer when present (at workflow, job, or step level).tagsvalues must be strings.trigger.schedulemust be a valid cron expression.jobsmust contain at least one job.- Job names must be unique within the workflow.
- Step names must be unique within each job.
- Each job must contain at least one step.
- Job dependency references must name existing jobs.
- Step dependency references must name existing steps within the same job.
- No cyclic dependencies among jobs.
- No cyclic dependencies among steps within each job.
${{ inputs.* }}expressions in model definitionglobalArgumentsmust reference inputs declared in the workflow'sinputsblock.
Complete Example
id: d14de37a-b931-4363-97e9-fe9ec9637b60
name: multi-stage
description: A multi-stage deployment pipeline
trigger:
schedule: "0 3 * * *"
tags:
team: platform
env: production
inputs:
type: object
properties:
environments:
type: array
description: Target environments
region:
type: string
default: us-east-1
required:
- environments
jobs:
- name: build
description: Compile and test
weight: 0
steps:
- name: compile
description: Build the artifacts
task:
type: model_method
modelIdOrName: builder
methodName: run
inputs:
target: "${{ inputs.region }}"
weight: 0
allowFailure: false
- name: test
description: Run tests
task:
type: model_method
modelIdOrName: test-runner
methodName: execute
dependsOn:
- step: compile
condition:
type: succeeded
weight: 1
allowFailure: false
- name: deploy
description: Deploy to each environment
dependsOn:
- job: build
condition:
type: succeeded
weight: 10
concurrency: 2
steps:
- name: deploy-env
description: Deploy to environment
task:
type: model_method
modelIdOrName: deployer
methodName: apply
forEach:
item: env
in: "${{ inputs.environments }}"
dataOutputOverrides:
- specName: result
lifetime: 30d
garbageCollection: 5
tags:
stage: deploy
vary:
- environment
- name: notify
description: Send notifications
dependsOn:
- job: deploy
condition:
type: completed
weight: 20
steps:
- name: send-notification
task:
type: workflow
workflowIdOrName: notification-workflow
inputs:
channel: "#deployments"
allowFailure: true
version: 1
reports:
require:
- name: deployment-report
methods:
- apply
- summary
skip:
- debug-reportHistory
Commands for inspecting past workflow runs.
swamp workflow history get <name>
Show the latest run for a workflow.
| Argument | Required | Description |
|---|---|---|
workflow_id_or_name |
Yes | Workflow ID or name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |
swamp workflow history search [query]
Search past workflow runs.
| Argument | Required | Description |
|---|---|---|
query |
No | Search query. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |
swamp workflow run search [query]
Search workflow runs with additional filtering options.
| Argument | Required | Description |
|---|---|---|
query |
No | Search query. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--since <duration> |
Only runs started within duration (1h, 1d, 7d, 1w, 1mo). |
--status <status> |
Filter by run status (pending, running, succeeded, failed). |
--workflow <name> |
Filter by workflow name. |
--tag <tag> |
Filter by tag (KEY=VALUE). Repeatable. |
--limit <n> |
Maximum results (default: 50). |
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |
swamp workflow history logs <run_id_or_workflow>
Show logs for a specific workflow run.
| Argument | Required | Description |
|---|---|---|
run_id_or_workflow |
Yes | Run ID or workflow name. |
| Flag | Description |
|---|---|
--repo-dir |
Path to the Swamp repository. |
--tail <n> |
Show only the last N lines. |
--server |
Target a remote swamp serve instance. |
--token |
Authentication token for the remote server. |