Skip to main content

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

SettingValue
Default path.loom/workflow.yml
Override--workflow <path> on check, compile, or run
Schema versionv1 (the only supported version)

Anatomy of a workflow

A workflow file is a YAML mapping with a fixed set of root-level keys:

KeyRequiredPurpose
versionYesSchema version — must be the exact string "v1"
stagesYesOrdered list of stage names that define execution phases
variablesNoWorkflow-level environment variables (defaults for all jobs)
defaultNoDefault job settings inherited by all jobs via deep merge
includeNoList of local template files to include ({local: .loom/templates/name.yml})
workflowNoOptional 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)
PhaseCommandQuestion it answersOutput
Validateloom checkIs this workflow valid?Pass/fail with error paths like WF_SCHEMA_V1 /my-job/target: ...
Compileloom compileWhat will Loom execute?Graph IR — a JSON representation of stages, nodes, edges, and resolved variables
Executeloom run --localWhat 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:

EntityPatternMax lengthExample
Stage name^[a-z][a-z0-9_-]{0,31}$32 charsci, build-assets, deploy_prod
Job name^[a-z][a-z0-9_-]{0,63}$64 charsunit-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 limitAPP_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.local paths point to files under .loom/templates/ with a .yml or .yaml extension. See Includes & templates.

Jobs run in an unexpected order

  • Cause: Misunderstanding stage ordering or the needs dependency mechanism.
  • Fix: A job that omits needs waits for all jobs in earlier declared stages. An explicit needs list replaces that barrier, and needs: [] permits immediate readiness. Jobs that are ready together may run concurrently. Run loom compile to inspect the resolved execution graph. See Stages, jobs, and steps.