Context and templating
Understand when run data is ready, how outputs move it, and how workflows use it safely.
Context is the data a workflow can use while it runs. It begins with the event or manual inputs and grows as jobs and steps produce results.
That timing matters. A workflow cannot read a result before the work that creates it has finished. Shipfox resolves a value when enough context exists. Values that depend on later work wait until that work is done.
Context arrives in stages
Think of context as a flow rather than one global object:
- Run creation adds the run identity, trigger, event, and inputs.
- Job readiness adds outputs from upstream jobs that have finished.
- Listening execution adds the event batch that woke that execution.
- Step dispatch adds earlier step outputs and retry context.
- Runner execution resolves secret bindings for the command that needs them without storing the plaintext value in the run plan.
Workspace variables and project settings provide long-lived configuration. Events and outputs describe this run. Secrets grant authority and follow a stricter path.
The Contexts reference is the canonical map of which context roots each field can read.
Templates carry data; predicates make decisions
A ${{ }} template places a value into text or settings. It can put an issue
title in a prompt. It can also bind a job output to an environment variable.
A predicate returns a boolean. Filters, conditions, gates, and job success rules use predicates. They decide whether work starts, continues, or passes.
Keeping these roles separate makes policy easier to inspect. Text substitution does not quietly decide control flow. A predicate cannot run a command or change outside state. The same saved context therefore produces the same decision.
Use templates to move a known value. Use a predicate when the workflow needs a yes-or-no rule. The Expressions reference defines the exact syntax and available fields.
Outputs make results explicit
Steps in one job can share files, but files do not cross a job boundary. An output turns a result into named workflow data that later work can read.
This complete workflow moves a version between two isolated jobs:
# yaml-language-server: $schema=https://www.shipfox.io/docs/workflow.schema.json
name: Pass a version between jobs
runner: ubuntu-latest
triggers:
manual:
source: manual
event: fire
jobs:
package:
outputs:
version: ${{ steps.read_version.outputs.version }}
steps:
- key: read_version
run: echo "version=$(git describe --tags --always)" >> "$SHIPFOX_OUTPUT"
outputs:
version: string
publish:
needs: package
steps:
- run: printf 'Publishing version %s\n' "${{ jobs.package.outputs.version }}"The step creates version. The package job publishes it. The publish
job declares the dependency and places the value in its command.
When a step declares a JSON array or object, an exact single-expression job
mapping keeps that structure available to downstream jobs. A downstream
expression such as jobs.review.outputs.findings[0].severity can therefore
read a nested field. Mixed templates still produce strings.
Outputs suit small facts. Files and build products belong in external artifact storage, with an output carrying their location. Step outputs defines declaration types and encoding. Job outputs defines job output limits.
Templates carry untrusted data
Events, inputs, and step outputs carry data the workflow did not write. A shell step reads that data as text, so it cannot run as a command. A few shell constructs re-read their own arguments and break that.
Nothing guards a prompt or a job setting this way. An event can plant orders for an agent. It can also pick the runner a job uses. Keep an event out of any choice you would not let a stranger make.
Secrets and variables describes storage, masking, and binding. Integrations, integration connections, and tools explains the external credential boundary.