Predefined variables
Every Loom job receives a set of predefined environment variables. This page is the complete reference for what Loom injects, how each variable behaves, and how to map variables when migrating from another CI system.
When to use this page
How to read the tables
| Column | Meaning |
|---|
| Scope | When the variable becomes available: pipeline (before any job runs), job (when a job starts), or step (when a step executes). |
| Stability | stable = contract, safe to depend on. best_effort = populated when context is available, may be empty. placeholder = fixed value today, will gain real semantics later. |
| Source | Where the value originates: executor (Loom runtime), provider (host/Docker provider), or workspace (derived from the repo/checkout). |
All variables are available in both host and Docker provider environments unless otherwise noted.
LOOM variables
These are Loom-native variables. Prefer these in workflows that only run on Loom.
Pipeline scope
| Variable | Value | Stability | Source |
|---|
LOOM_CI | Always "true" | stable | executor |
LOOM_RUN_ID | Unique ID for the loom run invocation | stable | executor |
LOOM_PIPELINE_ID | Pipeline (executor) ID | stable | executor |
LOOM_WORKFLOW_PATH | Path to the workflow file (empty if not resolvable) | stable | executor |
Job scope
| Variable | Value | Stability | Source |
|---|
LOOM_JOB_NAME | Job name (graph node ID from the workflow) | stable | executor |
LOOM_JOB_ID | Same as LOOM_JOB_NAME | stable | executor |
LOOM_JOB_STAGE | Job stage from the workflow stage: field | stable | executor |
LOOM_JOB_TARGET | Job target (for example linux) | stable | executor |
LOOM_JOB_IMAGE | Docker image when image: is set; absent otherwise | stable | executor |
LOOM_PROVIDER | host or docker | stable | executor |
LOOM_PROJECT_DIR | Isolated workspace root where the job runs | stable | provider |
CI compatibility variables
Loom provides CI_* variables for compatibility with scripts and tools that expect a CI-standard environment. Many mirror a LOOM_* counterpart.
Pipeline scope
| Variable | Value | Stability | Source |
|---|
CI | Always "true" | stable | executor |
CI_RUN_ID | Same as LOOM_RUN_ID | stable | executor |
CI_PIPELINE_ID | Pipeline (executor) ID | stable | executor |
CI_PIPELINE_IID | Same as CI_PIPELINE_ID | stable | executor |
CI_PIPELINE_STARTED_AT | RFC 3339 timestamp when the pipeline started | stable | executor |
CI_PIPELINE_STARTED_AT_SLUG | URL-safe slug of the started-at timestamp (max 63 bytes) | stable | executor |
Job identity
| Variable | Value | Stability | Source |
|---|
CI_JOB_NAME | Job name (graph node ID) | stable | executor |
CI_JOB_ID | Same as CI_JOB_NAME | stable | executor |
CI_JOB_STAGE | Job stage from the workflow stage: field | stable | executor |
CI_JOB_TARGET | Job target (for example linux) | stable | executor |
CI_JOB_IMAGE | Docker image when image: is set; absent otherwise | stable | executor |
Job runtime
| Variable | Value | Stability | Source |
|---|
CI_JOB_STATUS | Always "running" | stable | executor |
CI_JOB_TIMEOUT | Always "0" (timeout not implemented yet) | stable | executor |
CI_JOB_STARTED_AT | RFC 3339 timestamp when the job started | stable | executor |
CI_JOB_STARTED_AT_SLUG | URL-safe slug of the job started-at timestamp | stable | executor |
Stage and step
| Variable | Value | Stability | Source |
|---|
CI_STAGE_STARTED_AT | RFC 3339 timestamp when the stage started | stable | executor |
CI_STAGE_STARTED_AT_SLUG | URL-safe slug of the stage started-at timestamp | stable | executor |
CI_STEP_ID | Always "1" (single-shell model today) | placeholder | executor |
CI_STEP_STARTED_AT | RFC 3339 timestamp when the step started | stable | executor |
CI_STEP_STARTED_AT_SLUG | URL-safe slug of the step started-at timestamp | stable | executor |
Project and directory
| Variable | Value | Stability | Source |
|---|
CI_BUILDS_DIR | Top-level builds/workspaces directory | stable | provider |
CI_PROJECT_DIR | Workspace root where the job runs (same as LOOM_PROJECT_DIR) | stable | provider |
CI_PROJECT_NAME | Derived from workspace root directory name (best-effort) | best_effort | workspace |
CI_PROJECT_NAMESPACE | Same as CI_PROJECT_NAME today | best_effort | workspace |
CI_PROJECT_NAMESPACE_SLUG | URL-safe slug of the namespace (max 63 bytes) | best_effort | workspace |
CI_PROJECT_NAMESPACE_ID | Always "0" | placeholder | executor |
Runner
| Variable | Value | Stability | Source |
|---|
CI_RUNNER_ID | Always "loom-local" | stable | executor |
CI_RUNNER_VERSION | Always "local" | stable | executor |
Git commit
| Variable | Value | Stability | Source |
|---|
CI_COMMIT_SHA | Full HEAD SHA from the workspace snapshot | best_effort | workspace |
CI_COMMIT_SHORT_SHA | First 8 characters of CI_COMMIT_SHA | best_effort | workspace |
CI_COMMIT_BRANCH | Branch name when resolvable; empty for detached HEAD | best_effort | workspace |
User-configurable variable
This variable is not injected by Loom but has special runtime behavior when set via workflow variables:.
| Variable | Effect |
|---|
CI_DEBUG_TRACE | Set to "true" to enable verbose shell tracing (set -x). If any secret uses file: false, Loom hard-fails with SECRETS_UNSAFE_DEBUG_TRACE to prevent accidental secret leakage. |
Upstream CI → Loom mapping
Use this table when migrating from GitLab CI (or similar systems) and you encounter upstream variable names in existing scripts.
Variables Loom provides
| Upstream variable | Loom equivalent | Notes |
|---|
CI | CI | Both set to "true". |
CI_COMMIT_SHA | CI_COMMIT_SHA | Snapshot HEAD SHA. |
CI_COMMIT_SHORT_SHA | CI_COMMIT_SHORT_SHA | First 8 characters. |
CI_COMMIT_BRANCH | CI_COMMIT_BRANCH | Empty when HEAD is detached; prefer LOOM_* where possible. |
CI_JOB_NAME | CI_JOB_NAME / LOOM_JOB_NAME | Prefer LOOM_JOB_NAME in Loom-only scripts. |
CI_JOB_STAGE | CI_JOB_STAGE / LOOM_JOB_STAGE | Values come from the workflow stage: field. |
CI_PROJECT_DIR | CI_PROJECT_DIR / LOOM_PROJECT_DIR | Points at the isolated workspace root in local runs. |
CI_PIPELINE_ID | CI_PIPELINE_ID / LOOM_PIPELINE_ID | Loom provides both. |
CI_JOB_IMAGE | CI_JOB_IMAGE / LOOM_JOB_IMAGE | Set only for jobs that use image: (Docker provider). |
Variables Loom does not provide
Loom does not derive platform metadata variables (merge requests, actors, URLs, tags). If you need these values, pass them explicitly via workflow or job variables:.
| Upstream variable | What to do instead |
|---|
CI_MERGE_REQUEST_IID | Pass explicitly in variables: from your CI platform or compute in a pre-step. |
CI_COMMIT_TAG | Pass a tag name explicitly, or gate via your own variables. |
CI_PROJECT_URL | Pass explicitly (Loom does not assume your forge remote URL). |
CI_COMMIT_AUTHOR | Compute with git log --format='%an' in a script step, or pass from the outer CI system. |
CI_PIPELINE_SOURCE | Not applicable to local runs. Pass from the outer CI system if needed. |
Migration checklist
When porting a workflow from another CI provider to Loom:
- Audit variable usage. Search your scripts for
$CI_ references and check each one against the mapping table above.
- Replace with
LOOM_ equivalents where possible. LOOM_* variables are the canonical names and will always be supported.
- Pass missing variables explicitly. For any upstream variable without a Loom equivalent, add it to your workflow or job
variables: block.
- Test with a debug step. Add a temporary step to verify variables are present, then remove it before merging:
script:
- env | grep -E '^CI_|^LOOM_' | sort
Precedence
When a job defines a variables: entry with the same name as a predefined variable, the user-defined value wins. This lets you override any predefined variable for testing or migration purposes. See Concepts — Variables for the full precedence chain.
Freshness and contributions
The variable registry is defined in libs/loom-variables/. If you find a missing or incorrect variable: