Skip to main content

loom run

Execute a Loom or GitLab workflow locally. Recorded runs produce a receipt and runtime logs with pointers to job, step, and system-section output. Configuration, workspace, or observer setup can fail before a receipt is written; in that case, start with the CLI error on stderr.

Canonical command usage

Use the generated core CLI command reference for the exact loom run synopsis, flags, and displayed defaults.

Requirements

  • --local flag: required for all runs in the current release.
  • Docker: jobs with an image: field run inside Docker. The Docker daemon must be installed and reachable. See Docker provider.
  • Host shell: jobs without image: run directly on your host shell. See Host provider.

GitLab workflow input

Default discovery selects .loom/workflow.yml before the repository root .gitlab-ci.yml. Use --format gitlab with a custom GitLab filename and --context with a project-relative loom.gitlab-pipeline-context.v1 JSON file. An exact .gitlab-ci.yml basename is inferred automatically. Explicit formats never fall back to another parser.

The context file is limited to 1 MiB. Its variables and changes drive GitLab rules and expansion. runner_tags describes local runner capabilities used to satisfy or reject plan requirements; it is not job-selection data. Ambient CI variables and runtime settings do not affect selection. Grant a selected privileged service explicitly with repeatable --privileged-service <alias>. Blank, duplicate, unknown, and ambiguous aliases fail before provider setup.

GitLab execution is Linux-only. Hosted macOS, OIDC tokens, Pages, and environments are unsupported requirements. Optional manual jobs are reported and not executed. Report processing and artifact expiry are recorded limitations. workflow_skipped exits 0 with a visible skipped result; source_invalid and non_executable exit nonzero before providers start.

Output verbosity: --quiet, --verbose, --stream

These flags control terminal output only. Verbosity settings do not disable structured runtime logs under .loom/.runtime/logs/<run_id>/.

FlagBehavior
(default)Standard output — job started/finished, status, receipt path
--quietMinimal output — receipt path and final status only
--verboseAdditional metadata (useful when you don't want to open runtime logs yet)
--streamStream job stdout/stderr in real time (increases console volume substantially)

Execution concurrency: --parallel

By default, loom run --local starts one runnable job at a time. Set --parallel to a positive integer to allow up to that many runnable jobs to execute concurrently. Dependencies and stage barriers still determine when each job becomes runnable.

loom run --local --parallel 4

Explicit zero, negative, and malformed values are rejected before the workflow starts.

Environment variables

VariableValuesDescription
LOOM_DOCKER_WORKSPACE_MOUNTbind_mount, ephemeral_volumeDocker workspace mount mode. Overridden by --docker-workspace-mount flag

Docker workspace mount resolution order

  1. --docker-workspace-mount flag value (if set)
  2. LOOM_DOCKER_WORKSPACE_MOUNT environment variable (if set)
  3. Repo config: .loom/config.yml
  4. User config: ~/.config/loom/config.yml
  5. Default: ephemeral_volume

The config field is runtime.docker.workspaceMount. See Precedence and merge rules.

Outputs

Recorded runs produce a receipt and runtime logs. Jobs can also publish declared artifacts:

ArtifactLocationDescription
Receipt.loom/.runtime/receipts/loom-run-local-<run_id>.jsonStructured run metadata: status, exit code, command, logs pointer
Runtime logs.loom/.runtime/logs/<run_id>/Per-job structured logs, manifests, and event streams
Job artifacts.loom/.runtime/logs/<run_id>/jobs/<job_id>/artifacts/Files published from execution workspaces when artifacts: is configured

Runtime logs directory structure

.loom/.runtime/logs/<run_id>/
├── pipeline/
│ ├── summary.json # Overall pipeline status + exit code
│ └── manifest.json # Job pointers (find failing job here)
├── jobs/<job_id>/
│ ├── summary.json # Job-level status
│ ├── manifest.json # Step pointers (find failing step here)
│ ├── user/execution/script/<NN>/
│ │ ├── summary.json # Step status
│ │ └── events.jsonl # Step event stream
│ ├── system/<section>/
│ │ ├── summary.json # System section status
│ │ └── events.jsonl # System event stream
│ └── artifacts/ # Extracted job artifacts (when configured)
└── events.jsonl # Legacy compatibility

Start at pipeline/summary.json, then follow pointers in pipeline/manifest.json to the failing job and step.

Exit codes

CodeMeaning
0All jobs succeeded
1One or more jobs failed, or the run failed to start

When --cache-diff is used, exit code 1 also indicates mechanical divergence between the cache-off and cache-on runs.

Output streams

StreamContent
stderrStatus messages, receipt path, loom run passed / error summary
stdout(unused in normal operation)

Example output

Successful run

loom run passed
receipt: .loom/.runtime/receipts/loom-run-local-1772050684042246000.json

Failed run

When an executed job fails and Loom writes a receipt, it prints the receipt path before the error:

receipt: .loom/.runtime/receipts/loom-run-local-1772050684042246000.json
loom run: job "build" failed (exit code 2)

Examples

Run the default workflow

loom run --local

Run with streaming output

loom run --local --stream

Run up to four jobs concurrently

loom run --local --parallel 4

Run a custom workflow with verbose output

loom run --local --workflow ci/nightly.yml --verbose
loom run --local --format gitlab --workflow ci/pipeline.yml \
--context contexts/mr.json --privileged-service docker

Run with bind mount workspace mounting

loom run --local --docker-workspace-mount bind_mount

Cache differential analysis

loom run --local --cache-diff

Runs the workflow twice (first with cache disabled, then with cache enabled) and compares outputs. Mechanical divergence causes a non-zero exit. After collecting complete evidence for both executions, Loom quarantines a participating cache-hit candidate only when the node's declared outputs differ and exactly one eligible scope/key pair is associated with that node. It records ambiguous candidates without quarantine. This comparison does not establish universal cache safety. See Cache quarantine.

Failure triage

If no receipt was written, inspect the CLI error first. With a recorded run, distinguish between these failure types:

Provider / environment failure (Docker unavailable, image pull fails, permission errors):

  1. Open .loom/.runtime/logs/<run_id>/pipeline/summary.json
  2. Follow pointers in pipeline/manifest.json to the failing job
  3. Check jobs/<job_id>/system/<section>/events.jsonl

User step failure (your script: returned non-zero):

  1. Open .loom/.runtime/logs/<run_id>/pipeline/manifest.json
  2. Find the failing job, open jobs/<job_id>/manifest.json
  3. Follow failing_step_events_path to the step's events.jsonl

For the full pointer-first triage flow, see the Diagnostics ladder.

Provider selection

loom run selects a provider per job based on the workflow definition:

Job has image:ProviderDocumentation
YesDockerDocker provider
NoHost shellHost provider

Workflow-level caching (when configured) is documented at Cache.

  • loom check — validate the workflow schema before running
  • loom compile — inspect the compiled job graph without executing