Workflow
A workflow is a YAML file that defines the stages, jobs, scripts, variables, and provider settings for a Loom run. It can include local templates and apply shared defaults. Use loom check to validate the resolved workflow and loom compile to inspect its execution graph.
Workflow file location
| Setting | Value |
|---|---|
| Default path | .loom/workflow.yml |
| Override | --workflow <path> on check, compile, or run |
| Schema version | v1 (the only supported version) |
Anatomy of a workflow
A workflow file is a YAML mapping with a fixed set of root-level keys:
| Key | Required | Purpose |
|---|---|---|
version | Yes | Schema version — must be the exact string "v1" |
stages | Yes | Ordered list of stage names that define execution phases |
variables | No | Workflow-level environment variables (defaults for all jobs) |
default | No | Default job settings inherited by all jobs via deep merge |
include | No | List of local template files to include ({local: .loom/templates/name.yml}) |
workflow | No | Optional workflow gate; non-empty if rules are Experimental. See Rules |
Any other top-level key that matches the job name pattern (^[a-z][a-z0-9_-]{0,63}$) is treated as a job definition. Template jobs (names starting with .) are used for inheritance via extends but are not executed directly.
Minimal example
version: v1
stages: [ci]
check:
stage: ci
target: linux
script:
- echo "hello from Loom"
This defines one stage (ci) and one job (check) that runs a single shell command.
Multi-stage example
version: v1
stages: [build, test, deploy]
variables:
APP_ENV: "staging"
default:
target: linux
compile:
stage: build
script:
- make build
unit-tests:
stage: test
image: golang:1.22
script:
- go test ./...
integration:
stage: test
needs: [compile]
image: golang:1.22
script:
- go test -tags=integration ./...
ship:
stage: deploy
script:
- make deploy
Jobs that omit needs wait for every executable job in every earlier declared
stage. Jobs in the same stage have no implicit dependency and may run concurrently
when capacity permits. The explicit needs: [compile] on integration replaces
its stage barrier, so it waits for compile only. Because ship omits needs, it
waits for compile, unit-tests, and integration.
The workflow lifecycle
A workflow passes through three phases, each answering a different question:
loom check loom compile loom run --local
┌───────────┐ ┌─────────────┐ ┌────────────────┐
│ "Is this │ yes │ "What will │ ok │ "What │
│ valid?" │──────► │ Loom run?" │─────► │ happened?" │
└───────────┘ └─────────────┘ └────────────────┘
Schema + Graph IR Runtime logs
static (compiled JSON + receipt
validation representation)
| Phase | Command | Question it answers | Output |
|---|---|---|---|
| Validate | loom check | Is this workflow valid? | Pass/fail with error paths like WF_SCHEMA_V1 /my-job/target: ... |
| Compile | loom compile | What will Loom execute? | Graph IR — a JSON representation of stages, nodes, edges, and resolved variables |
| Execute | loom run --local | What happened during execution? | Runtime logs under .loom/.runtime/logs/<run_id>/ and a receipt under .loom/.runtime/receipts/ |
Start with loom check — it is the fastest feedback loop. Run it before every commit to catch schema errors, missing keys, and invalid references without waiting for execution.
Naming rules
The schema enforces strict naming conventions to keep workflows machine-parseable and consistent:
| Entity | Pattern | Max length | Example |
|---|---|---|---|
| Stage name | ^[a-z][a-z0-9_-]{0,31}$ | 32 chars | ci, build-assets, deploy_prod |
| Job name | ^[a-z][a-z0-9_-]{0,63}$ | 64 chars | unit-tests, lint_go |
| Template job | ^\.[a-z][a-z0-9_-]{0,63}$ | 65 chars (including .) | .base-job |
| Variable key | ^[A-Z_][A-Z0-9_]*$ | No hard limit | APP_ENV, CI_DEBUG |
Common pitfalls
loom check fails with "unknown field" or "schema validation error"
- Cause: Your YAML uses keys that are not in the workflow schema v1, or values have the wrong type.
- Fix: Compare your YAML to the root-level key table above. Variable values must be scalars; lists and mappings are rejected. Quote values such as
"3"to make their intended text clear, although unquoted numeric and boolean scalars are also accepted. Job keys must come from the allowed set:stage,target,script,extends,needs,image,runner_pool,variables,secrets,invariant,cache,services,artifacts.
Includes or templates don't resolve
- Cause: Template files are outside
.loom/templates/or use..path traversal. - Fix: Ensure
include.localpaths point to files under.loom/templates/with a.ymlor.yamlextension. See Includes & templates.
Jobs run in an unexpected order
- Cause: Misunderstanding stage ordering or the
needsdependency mechanism. - Fix: A job that omits
needswaits for all jobs in earlier declared stages. An explicitneedslist replaces that barrier, andneeds: []permits immediate readiness. Jobs that are ready together may run concurrently. Runloom compileto inspect the resolved execution graph. See Stages, jobs, and steps.
What to read next
- Structure your workflow: Stages, jobs, and steps
- Understand execution environments: Providers
- Parameterize with variables: Variables
- Full syntax reference: Syntax v1, Workflow schema v1
- Run your first workflow: Hello Loom