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
--localflag: 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>/.
| Flag | Behavior |
|---|---|
| (default) | Standard output — job started/finished, status, receipt path |
--quiet | Minimal output — receipt path and final status only |
--verbose | Additional metadata (useful when you don't want to open runtime logs yet) |
--stream | Stream 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
| Variable | Values | Description |
|---|---|---|
LOOM_DOCKER_WORKSPACE_MOUNT | bind_mount, ephemeral_volume | Docker workspace mount mode. Overridden by --docker-workspace-mount flag |
Docker workspace mount resolution order
--docker-workspace-mountflag value (if set)LOOM_DOCKER_WORKSPACE_MOUNTenvironment variable (if set)- Repo config:
.loom/config.yml - User config:
~/.config/loom/config.yml - 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:
| Artifact | Location | Description |
|---|---|---|
| Receipt | .loom/.runtime/receipts/loom-run-local-<run_id>.json | Structured 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
| Code | Meaning |
|---|---|
0 | All jobs succeeded |
1 | One 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
| Stream | Content |
|---|---|
| stderr | Status 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
Run a GitLab workflow with explicit service consent
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):
- Open
.loom/.runtime/logs/<run_id>/pipeline/summary.json - Follow pointers in
pipeline/manifest.jsonto the failing job - Check
jobs/<job_id>/system/<section>/events.jsonl
User step failure (your script: returned non-zero):
- Open
.loom/.runtime/logs/<run_id>/pipeline/manifest.json - Find the failing job, open
jobs/<job_id>/manifest.json - Follow
failing_step_events_pathto the step'sevents.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: | Provider | Documentation |
|---|---|---|
| Yes | Docker | Docker provider |
| No | Host shell | Host provider |
Workflow-level caching (when configured) is documented at Cache.
Related commands
loom check— validate the workflow schema before runningloom compile— inspect the compiled job graph without executing