loom run
Execute a Loom or GitLab workflow locally. Recorded runs produce state.jsonl
and events.jsonl under .loom/.runtime/runs/<run_id>/. Use
loom logs to inspect state and output after execution.
Canonical command usage
Use the
core CLI command reference for the exact
loom run synopsis, flags, and displayed defaults.
Requirements
- Local execution: every run executes on this machine. The historical
--localflag is still accepted but hidden and ignored; it is no longer required. - 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.
Select jobs
Use repeatable --job to execute source jobs or exact final job IDs and their transitive ordering and
artifact prerequisites, for either source format:
loom run --job consumer --job smoke
Omitting both selectors selects the full workflow. IDs are exact: commas are literal, surrounding whitespace is rejected, and duplicate IDs form one set. Unknown or unavailable jobs fail before snapshot materialization or provider work. Selection preserves workflow/job rules and existing dependency behavior.
An explicit request prints job selection with sorted requested_jobs, optional requested_instances,
effective_jobs and a stable scope identity; quiet suppresses the console line.
The same protected event is retained once in events.jsonl, from the final
snapshot or a terminal preflight outcome. A selected run verifies that scope,
not the entire workflow. Unrelated jobs are excluded.
For integer fanout, --job shard selects all source members before prerequisite
closure. --instance 'shard 2/3' selects only that exact final node, even when its
name is also a source alias. Repeat or combine both flags. job_selection binds
both request sets and effective jobs into its identity. See
integer fanout.
Native caller variables
Native --context accepts the strict
caller variable envelope. Values override
composed root/job values, including empty strings. Exported directory overrides
do not change physical cwd or mounts. Native contexts work for synchronous CLI
runs and MCP synchronous/server-owned runs. Config defaults.context remains
GitLab-only.
GitLab workflow input
Omitted flags fall back to the defaults block of the layered runtime config
(see runtime config syntax); explicit flags
always win.
Default discovery selects .loom/workflow.yml, then .loom/workflow.yaml,
before the repository root .gitlab-ci.yml. With --workflow and no
--format, Loom infers the format from the file content (a root version key
or a job target means Loom; other ordinary mappings mean GitLab). Include-only
mappings are ambiguous; an explicit context envelope hints its frontend. Use --format to override inference and
--context with a project-relative loom.gitlab-pipeline-context.v1 JSON file
or a short name that resolves to .loom/contexts/<name>.json (see
loom context init). 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.
changes.paths is optional. When a matched rules:changes has no explicit
context paths, loom run resolves changed paths the same way as
loom check: git diff --merge-base in the repository root against the rule's compare_to or
changes.base, comparing the working tree when compare_to is set and the
context has no changes. Fail-closed cases (unknown ref, shallow clone, no git)
stop before providers start with GL_RULE_CHANGES_UNAVAILABLE. The resolved comparisons determine the selected execution plan.
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 recording under .loom/.runtime/runs/<run_id>/.
The CLI prints the run locator before preparing the isolated workspace. Normal
output reports rooted preflight, snapshot preparation and compilation; --verbose also shows real
clone, reference, checkout and overlay phase boundaries with elapsed time. Overlay
counts report successful entries; reference counts report the inventory selected
for preservation, including when transfer later fails. --quiet suppresses preparation commentary but
retains the early run locator and final outcome. Quiet takes precedence when both
quiet and verbose are set. Recorded preparation events remain available in every
mode and describe actual work boundaries, not process liveness.
Invalid source and known blocked, skipped or unsupported cache-diff GitLab plans finish before snapshot materialization. Their run records preserve the planning outcome and diagnostics. Accepted source is compiled again from the isolated snapshot before execution; the initial mutable-source check never authorizes a job to run.
| Flag | Behavior |
|---|---|
| (default) | Standard output — job started/finished, status, run directory |
--quiet | Minimal output — run directory, summary and final status |
--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.
Optional Loom Web evidence upload
Uploads are disabled by default. Use project setup and
project authorization, explicitly choosing
web.upload: true, to use a bound local project credential. Authentication alone
does not enable delivery. The upload guide describes
credential selection, offline preparation and saved-run retry.
For CI mode, provide the complete host environment tuple:
export LOOM_WEB_UPLOAD=true
export LOOM_WEB_URL=https://loom.example.com
export LOOM_WEB_PROJECT_ID=11111111-1111-4111-8111-111111111111
export LOOM_WEB_TOKEN_ENV=LOOM_INGEST_TOKEN
# Supply LOOM_INGEST_TOKEN through your existing secret-management environment.
export LOOM_WEB_REDACT_ENV=DEPLOY_TOKEN,PACKAGE_TOKEN
loom run
An explicitly supplied LOOM_WEB_UPLOAD value other than exactly true disables
upload, including credential lookup and network operations. Any CI destination or
token selector requires explicit consent and the complete tuple; partial modes
never borrow fields from repository configuration or the local credential store.
The destination and selected credential are bound before workflow execution.
LOOM_WEB_URL must be an HTTPS origin without a path, query, fragment or user
information. TLS verification is required; redirects and ambient proxies are
disabled. Upload uses a project-bound ingest:write credential and never starts
browser/device login.
The current Web upload protocol handles previously saved native artifacts. The two-file workflow recording format is retained locally; upload does not convert it to an older receipt format. An upload warning does not change the workflow outcome or exit code.
Outputs
Each recorded invocation writes:
.loom/.runtime/runs/<run_id>/
state.jsonl
events.jsonl
State records describe workflow, job, step and command transitions. Event records carry contextual messages and stdout/stderr output. The CLI prints the run location and file paths, including for failures after recording starts.
loom logs <run_id> --state
loom logs <run_id> --tail 30
loom logs <run_id> --json
See Runtime logs for record fields.
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, run directory, loom run passed / error summary |
| stdout | (unused in normal operation) |
Example output
run: .loom/.runtime/runs/<run_id>
state: .loom/.runtime/runs/<run_id>/state.jsonl
events: .loom/.runtime/runs/<run_id>/events.jsonl
loom run passed
For an executed job failure, Loom also prints the first failing job's output tail
and a pointer to events.jsonl. --quiet omits that tail.
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
Read the state transitions to identify the failed job, then inspect its events:
loom logs <run_id> --state
loom logs <run_id> --tail 30
JSON output retains context for filtering by job or step. If recording could not start, inspect the CLI error. 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 logs— inspect a recorded runloom check— validate the workflow schema before runningloom compile— inspect the compiled job graph without executing