Workflow YAML Schema Reference for Shipfox
Complete reference for the Shipfox workflow YAML schema: top-level fields, triggers, jobs, run and agent steps, gates, environment variables, and validation rules.
This page is the authoritative reference for the Shipfox workflow YAML schema. Workflow files live at .shipfox/workflows/*.yml. The schema is strict: unknown keys are rejected everywhere. Use this reference when authoring or debugging workflow files.
Top-level fields
| Field | Type | Description |
|---|---|---|
name | string | Required. Human-readable workflow name. Must be at least one character. |
runner | string | string[] | Default runner label or list of labels for all jobs in this workflow. Individual jobs can override this value with their own runner field. See Runners and execution environments for how labels route jobs. |
env | object | Environment variables applied to run steps across the entire workflow. See Environment variables. |
triggers | object | Named trigger entries that define when this workflow runs. Each key is a user-chosen trigger name; each value is a trigger object (see Trigger fields). If present, at least one entry is required. |
jobs | object | Required. Named job entries that define the work to perform. Each key is a user-chosen job name; each value is a job object (see Job fields). At least one entry is required. |
Trigger fields
| Field | Type | Description |
|---|---|---|
source | string | Required. Where the event comes from. For an integration this is the integration connection slug (e.g., github_acme, sentry_acme), not the bare provider name; a source that matches no integration connection slug is stored but never fires. Built-in sources use the literal strings manual and cron. See the Trigger Sources reference. |
event | string | Required. The event name from the named source (e.g., push, issue.created, fire, tick). |
filter | string | A CEL expression that narrows which events fire this trigger (e.g., event.ref == "refs/heads/main"). Evaluated when the event arrives: a false result skips the event, and an expression error fails closed (no run). Not allowed on manual or cron triggers. See Trigger filters. |
with | object | Extra values recorded on the trigger and passed to every run it starts. Steps read them as ${{ inputs.* }}; see Expressions. |
config | object | Source-specific trigger settings. Cron uses schedule and optional timezone; see Cron triggers. Other shipped sources do not accept this field. |
Job fields
| Field | Type | Description |
|---|---|---|
steps | array | Required. The ordered list of steps this job executes. At least one step is required. Each step is either a run step or an agent step; see Step fields. |
needs | string | string[] | Name(s) of other jobs that must complete successfully before this job starts. Jobs without needs run in parallel by default. |
runner | string | string[] | Runner label(s) for this specific job. Overrides the workflow-level runner if set. |
env | object | Environment variables scoped to this job's run steps. See Environment variables. |
execution_timeout | string | Maximum duration allowed for this job before it is cancelled (e.g., "30m", "2h"). Defaults to 6 hours; see Limits. |
success | string | A CEL expression that decides whether the job succeeded once all its executions settle, with executions in scope (each element carries index, name, status, events, outputs, and timing fields). Defaults to succeeding when no execution failed, including an empty listener. See Expressions. |
if | string | One ${{ }}-wrapped CEL boolean evaluated when the job would start. False (or an evaluation error) marks the job skipped. See Conditionals. |
outputs | object | Named values this job exposes to downstream jobs. Each value is a ${{ }} template (typically over steps.<key>.outputs) that must resolve to a scalar. Downstream jobs read them through needs and jobs. |
checkout | object | Declares the job's repository-checkout intent: permissions.contents (read or write, default read) and persist-credentials (boolean, default true, which keeps the checkout credential available to later steps). Omit the block for a read-only checkout with persisted credentials. |
listening | object | Turns the job into a listening job that fires one execution per matching event batch until it resolves. See Listening fields. |
name | string | Optional display name for this job, shown in the dashboard run view. Supports ${{ }} templates. |
Checkout fields
The optional job checkout block controls the repository credential used for
that job. It does not control integration tools.
| Field | Type | Description |
|---|---|---|
permissions.contents | 'read' | 'write' | Repository-content access requested for checkout and later Git commands. Defaults to read. |
persist-credentials | boolean | Keeps the checkout credential available to later steps when true. Defaults to true. Set it to false when no step should run authenticated Git commands after checkout. |
Step fields
A step is either a run step or an agent step. The kind is determined by which keys are present: run marks a run step; prompt (or any agent field) marks an agent step. The two kinds share one strict object, so unknown keys are always rejected and mixing kinds on one step is a validation error (see Validation rules).
Run step fields
| Field | Type | Description |
|---|---|---|
run | string | Required. The shell command to execute on the runner. Must be at least one character. |
key | string | Stable identifier for this step. This is what gate.on_failure.restart_from and steps.<key> references match; a step that a gate restarts from must have a key. |
name | string | Display label shown in the dashboard log. Display-only; it cannot be referenced by restart_from. Supports ${{ }} templates. |
if | string | One ${{ }}-wrapped CEL boolean. False marks the step skipped. See Conditionals. |
env | object | Step-level environment variables. See Environment variables. |
outputs | object | Typed outputs this step exposes. See Step outputs. |
gate | object | Gate and retry configuration. See Gate fields. |
Agent step fields
| Field | Type | Description |
|---|---|---|
prompt | string | Required. The instruction given to the AI model. Must be at least one character. Supports ${{ }} interpolation; see Expressions. |
model | string | Model ID to use for this step (e.g., claude-opus-4-8, gpt-5.5-pro). Uses the workspace default if omitted. Model catalog checks run at execution time, not at parse time. |
harness | string | The agent harness that executes this step. Accepted values: pi (default) or claude (the Claude Agent SDK). The harness determines which thinking values are accepted. |
thinking | string | Reasoning depth for the model. Accepted values depend on the harness. pi: off, minimal, low, medium, high, xhigh; claude: low, medium, high, xhigh, max. Default: xhigh. |
provider | string | Provider ID for the model (e.g., anthropic, openai). Uses the workspace default if omitted. See Model Providers for the full catalog and resolution rules. |
tools | string[] | Non-empty allowlist of harness-native tool names. Names depend on the selected harness and deployment configuration. |
integrations | object[] | Non-empty list of integration-tool selections exposed through MCP. See Agent integration fields. |
key | string | Stable identifier for this step, matched by gate.on_failure.restart_from and steps.<key> references. |
name | string | Display label shown in the dashboard log. Display-only. Supports ${{ }} templates. |
if | string | One ${{ }}-wrapped CEL boolean. False marks the step skipped. See Conditionals. |
outputs | object | Typed outputs this step exposes; the agent receives a set_output tool for each declared key. See Step outputs. |
gate | object | Gate and retry configuration. See Gate fields. |
Agent integration fields
Each entry under an agent step's integrations list selects tools from one
workspace integration connection. Selection is validated when the workflow syncs.
| Field | Type | Description |
|---|---|---|
include | string[] | Required. Non-empty tool selection. A token may select one tool family, one method as family.method, or every available family with *. |
connection | string | Integration connection slug. Defaults to the project's source integration connection. The integration connection must belong to the workspace and support agent tools. |
exclude | string[] | Non-empty selections removed after include is expanded. Useful with a family or * selection. |
allow_write | boolean | Explicit opt-in for write-capable selections. Defaults to false; a write-capable include is rejected unless this is true. |
repos | string[] | Non-empty repository identifiers available to the selected tools. Defaults to the project repository. |
The workflow receives the selected operations, not the integration connection credential. Use the smallest selection and repository scope that can complete the task. See Use integration tools for a worked example.
Gate fields
A gate block is optional on both step kinds and must contain at least one of success or on_failure.
| Field | Type | Description |
|---|---|---|
success | string | A CEL expression evaluated after the step completes. In scope: step.exit_code (integer), step.status (string), and step.outputs (the step's declared outputs). Example: step.exit_code == 0. A missing exit code or an expression error makes the gate uncheckable, which fails the job without restarting; see Feedback loops. |
on_failure.restart_from | string | The key of an earlier step in the same job to restart from when the gate fails. The referenced step must appear before this step in the job's step list and must have a key (name: is display-only and cannot be referenced). |
on_failure.feedback | string | Optional message recorded with the restart decision. Supports ${{ }} templates (including steps.<key>.outputs). The restarted step reads it as ${{ step.restart.feedback }}. Defaults to gate condition not met. |
This gate fragment belongs on a step whose earlier edit step has a key:
gate:
success: step.exit_code == 0
on_failure:
restart_from: edit
feedback: Tests are still failing; try again.Step outputs
A step's outputs: block declares typed outputs: each key maps to a type
(string, number, boolean, or json). For json, an object form
{type: json, schema: <JSON Schema>} validates the value. Keys must be CEL
identifiers (^[a-zA-Z_][a-zA-Z0-9_]*$).
How a step produces the values:
- Run steps append
key=valuelines to the file named by theSHIPFOX_OUTPUTenvironment variable. Multi-line values use the heredoc formkey<<DELIM ... DELIM. - Agent steps receive a
set_outputtool for the declared keys. If the agent finishes without setting a declared output, it is re-prompted a bounded number of times, then the step fails.
Outputs are read on success only, coerced against the declared types, and
exposed to later expressions as steps.<key>.outputs.<name>. Job outputs
templates lift them to downstream jobs. Size caps live in
Limits.
This jobs fragment belongs under an existing workflow's jobs map:
jobs:
build:
outputs:
version: ${{ steps.compile.outputs.version }}
steps:
- key: compile
run: echo "version=$(git describe --tags)" >> "$SHIPFOX_OUTPUT"
outputs:
version: string
release:
needs: build
steps:
- env:
VERSION: "${{ jobs.build.outputs.version }}"
run: ./release.sh "$VERSION"Listening fields
The listening block turns a job into a listening job:
after its needs are met, the job activates and fires one execution per
matching event batch until it resolves. on and until entries use the same
{source, event, filter?, with?} shape as workflow triggers,
except config is rejected.
| Field | Type | Description |
|---|---|---|
on | trigger[] | Required. The events that start an execution. At least one entry. |
until | trigger[] | Events that resolve the job. |
timeout | string | Maximum wall-clock listening time (duration string, e.g. "24h"). Capped by the run timeout. |
max_executions | number | Caps how many executions the job fires before it resolves. |
batch.debounce | string | Quiet window after an event before an execution starts; later events join the batch. |
batch.max_size | number | Maximum events coalesced into one batch. |
batch.max_wait | string | Longest a batch waits before flushing, debounce notwithstanding. |
on_resolve | 'finish' | 'cancel' | What happens to an in-flight execution when until fires: finish lets it complete (default), cancel cancels it. |
Validation: a listening job must declare at least one resolution source
(until, timeout, or max_executions), and batch needs at least one of
its three fields.
Environment variables
env maps exist at three levels (workflow, job, and step) and all follow the same rules:
- Run steps only.
envis invalid on an agent step, and workflow-level and job-levelenvis not forwarded to agent steps. - Nearest scope wins. When the same name is defined at several levels: step
env> jobenv> workflowenv. - POSIX names. Keys must match
[A-Za-z_][A-Za-z0-9_]*. - Values stringify. A value may be a string, number, or boolean; numbers and booleans are stringified before a run is saved.
- No unset syntax.
FOO: ""sets an empty string; an emptyenv: {}block does not remove variables from the runner's process environment. - Values may interpolate.
${{ }}expressions resolve when their referenced context becomes available; see Expressions. - Size caps. Each
envmap holds at most 128 entries and 32 KiB serialized; see Limits.
Never hardcode a secret value in env: the workflow file is committed to
your repository. Store it as a workspace secret
and bind it with ${{ secrets.KEY }}; the runner pulls the value at
execution time and masks it in logs.
Constraints table
| Field | Run step | Agent step |
|---|---|---|
run | ✅ Required | ❌ Not allowed |
prompt | ❌ Not allowed | ✅ Required |
model | ❌ Not allowed | ✅ Optional |
harness | ❌ Not allowed | ✅ Optional |
thinking | ❌ Not allowed | ✅ Optional |
provider | ❌ Not allowed | ✅ Optional |
tools | ❌ Not allowed | ✅ Optional |
integrations | ❌ Not allowed | ✅ Optional |
env | ✅ Optional | ❌ Not allowed |
key | ✅ Optional | ✅ Optional |
name | ✅ Optional | ✅ Optional |
if | ✅ Optional | ✅ Optional |
outputs | ✅ Optional | ✅ Optional |
gate | ✅ Optional | ✅ Optional |
Validation rules
The schema is strict. Every one of these rules is enforced at parse time; violating any of them produces a typed validation error.
- Unknown keys are rejected everywhere.
- A step must define
runORprompt: never both, never neither. model,harness,thinking,provider,tools, andintegrationsare invalid on a run step. They may only appear alongsideprompt.envis invalid on an agent step. Workflow-level and job-level env is not applied to agent steps either.promptis required on any step that setsmodel,harness,thinking,provider,tools, orintegrations.- Write-capable integration tools require
allow_write: true. Unknown integration connections, tools, and methods are rejected during sync. - At most one
manualtrigger per workflow. Multiplesource: manualentries are not permitted, and amanualtrigger cannot carry afilter. restart_frommust reference an earlier keyed step in the same job.ifmust be exactly one${{ }}-wrapped expression. Bare CEL or mixed text is rejected.- A listening job must declare a resolution source. At least one of
until,timeout, ormax_executions; see Listening fields. - The
agent:key is reserved and rejected. It is not a valid alias for an agent step; usepromptinstead. jobsandtriggersobjects must have at least one entry. An empty object for either field is invalid.
Full example
This complete workflow requires a Node.js repository, an online
ubuntu-latest runner, configured Anthropic credentials, and a GitHub
integration connection. Replace github_acme with that integration connection's slug before syncing
it.
name: Full example
runner: ubuntu-latest
env:
NODE_ENV: test
triggers:
on_demand:
source: manual
event: fire
on_push:
source: github_acme # your GitHub integration connection slug
event: push
jobs:
test:
steps:
- key: install
run: npm ci
- run: npm test
gate:
success: step.exit_code == 0
on_failure:
restart_from: install
review:
needs: test
steps:
- key: review
model: claude-opus-4-8
thinking: high
prompt: Review the repository and suggest improvements.Related pages
Expressions (CEL)
Gate and job success predicates, and ${{ }} interpolation.
Model Providers
Every provider ID, default model, and how agent config resolves.
Limits
Every default and cap: attempts, timeouts, log budgets, env sizes.