Receipts contract
Receipts are the stable entry point for Loom run diagnostics. Loom writes them under .loom/.runtime/receipts/ and prints the resulting file path to the terminal so you can jump directly into the right runtime artifacts without reconstructing paths by hand.
Where receipts live
.loom/.runtime/receipts/<filename>.json
Current loom run --local filenames use this pattern:
loom-run-local-<nanosecond-timestamp>.json
Example:
loom-run-local-1772865600000000000.json
Treat the filename as an implementation detail. Use the path printed by the CLI rather than constructing the name yourself.
What the CLI prints
At the end of a run, Loom prints the receipt path in this form:
receipt: /absolute/path/to/repo/.loom/.runtime/receipts/loom-run-local-1772865600000000000.json
The current implementation prints the absolute path.
Receipt schema (v1)
The receipt is JSON. The field list below describes the current loom run --local receipt shape. Other receipt kinds may use the same schema_version and kind pattern with a smaller field set.
Core fields
| Field | Type | Description |
|---|---|---|
schema_version | string | Always v1 |
kind | string | Currently loom-run-local for local runs |
command | string[] | Exact CLI invocation |
repo_root | string | Absolute repository root |
workflow_path | string | Absolute workflow path |
started_at, finished_at | string | RFC 3339 timestamps |
duration_ms | integer | Wall-clock runtime in milliseconds |
status | string | Current values are success or failure |
exit_code | integer | Process exit code |
error | string | Present when Loom surfaces a run error |
Runtime pointer fields
| Field | Type | Description |
|---|---|---|
logs_dir | string | Absolute path to .loom/.runtime/logs/<run_id>/ |
events_jsonl_path | string | Absolute path to the run-scoped events.jsonl |
phase_report_path | string | Absolute path to phase-report.json for the run |
Execution context fields
| Field | Type | Description |
|---|---|---|
snapshot_path | string | Snapshot or isolated workspace path when present |
snapshot_head_sha | string | Git revision used for the snapshot when present |
graph_ir | object | Resolved graph IR when available |
executor_receipt | object | Executor order and per-node outcomes when available |
warnings | object[] | Validation or runtime warnings when present |
cache_diff | object | Cache-diff receipt block when that mode is enabled |
Workflow rule decision evidence
When resolved Graph IR contains an explicit workflow_rules contract,
executor_receipt.WorkflowRuleDecision contains additive, value-safe evidence:
| Field | Type | Description |
|---|---|---|
schema_version | string | Always loom.workflow-rule-decision.v1 |
outcome | string | allow, deny, or evaluation_error |
reason | string | Stable category: rule_matched, no_rules, no_matching_rule, or evaluation_failed |
rule_index | integer | Zero-based decisive/failing rule, or -1 when no individual rule applies |
diagnostic_code | string | Present only for a typed evaluation failure |
The state mapping is deterministic:
| Outcome | Reason | Rule index |
|---|---|---|
allow | rule_matched | Matched rule index |
allow | no_rules | -1 |
deny | no_matching_rule | -1 |
evaluation_error | evaluation_failed | Failing rule index, or -1 for a contract-level failure |
The enclosing receipt remains schema_version: "v1"; the nested evidence version makes its additive
contract explicit. Graphs that omit workflow_rules omit WorkflowRuleDecision, preserving legacy
receipt shape. Evidence never includes expressions, literals, variable names or values, or evaluator
error text.
Executor node outcomes
Each executor_receipt.Nodes[] entry keeps the aggregate Status, ExitCode, and Error fields, records execution identity, and adds two phase-specific outcomes:
| Field | Description |
|---|---|
DeclaredTarget | Target retained in resolved Graph IR. This is a declaration, not an observation. |
SelectedProvider | Provider selected for the node (host or docker). Selection does not prove runtime OS or architecture. |
ObservedRuntime | Present only after the provider reports an observation from a started execution context. See the identity contract below. |
Command | Command/provider status, exit code, and error. A successful command remains success when later artifact publication fails. |
ArtifactPublication | Present for a declared artifact set. Records status, requirement, source kind/root, destination root, file count, skip reason, archive metadata, and typed failure operation/path/error. |
ArtifactPublication.Status is success, failed, skipped, not_run, or pending. A skipped optional no-match uses SkipReason: "no_matching_files". A command-success/publication-failure node has Command.Status: "success", ArtifactPublication.Status: "failed", and aggregate Status: "failed".
If temporary file-secret cleanup fails, an otherwise-due publication is blocked with Operation: "cleanup"; artifacts.when eligibility still comes from the command/provider outcome, so a declaration that was not due remains skipped.
For SourceKind: "provider_export", SourceRoot identifies the transient staging root used for matching and diagnosis; Loom removes that root during finalization. DestinationRoot remains the persistent final directory.
Observed runtime identity
ObservedRuntime is execution evidence and never participates in provider
selection, target negotiation, or preflight policy:
| Field | Description |
|---|---|
OS | Observed operating-system identifier. Empty or absent means unknown. |
Architecture | Host: Go architecture of the executing Loom process. Docker: OCI architecture reported for the exact image used by the started container. |
Source | host_process or docker_container_image. |
UnavailableReason | Safe reason that one or more observation values could not be collected. Empty values remain unknown and are never filled from declarations. |
The host provider records runtime.GOOS and runtime.GOARCH only after the
job child process starts. A missing shell, canceled pre-start command, rejected
node, skipped node, or later unstarted node has no ObservedRuntime. A started
host node retains its observation when the command or later finalization fails.
The Docker provider observes only after the main job container starts. It
inspects that container to resolve its exact image identity, then inspects that
image for OS and OCI architecture. It does not use the Docker daemon's OS or
architecture as container evidence. If either inspection fails or omits a
value, the receipt keeps the available values and records
UnavailableReason; collection failure does not change the command outcome.
Docker image architecture describes the selected container image, not the host
CPU. It cannot prove that execution was native, cannot rule out QEMU or another
emulation layer, and does not attest to individual binaries in the image.
Likewise, host GOARCH identifies the Loom process architecture and may differ
from physical hardware when Loom itself runs through translation.
DeclaredTarget, SelectedProvider, and ObservedRuntime are deliberately
independent. Loom never copies a declared target, a provider capability list,
or Docker daemon architecture into observed fields.
Example
{
"schema_version": "v1",
"kind": "loom-run-local",
"command": ["loom", "run", "--local", "--workflow", ".loom/workflow.yml"],
"repo_root": "/Users/you/project",
"workflow_path": "/Users/you/project/.loom/workflow.yml",
"snapshot_path": "/private/var/folders/.../loom-run-local-1772865600000000000",
"snapshot_head_sha": "9304d9b815cae5c49ac4b5c987532d101c948408",
"logs_dir": "/Users/you/project/.loom/.runtime/logs/loom-run-local-1772865600000000000",
"events_jsonl_path": "/Users/you/project/.loom/.runtime/logs/loom-run-local-1772865600000000000/events.jsonl",
"phase_report_path": "/Users/you/project/.loom/.runtime/logs/loom-run-local-1772865600000000000/phase-report.json",
"started_at": "2026-03-07T12:00:00Z",
"finished_at": "2026-03-07T12:00:12Z",
"duration_ms": 12000,
"status": "success",
"exit_code": 0
}
Pointer-first usage
Follow these fields in order:
| Field | What it answers | Next step |
|---|---|---|
status, exit_code | Did the run succeed? | If not, open logs_dir |
logs_dir | Where are the pipeline and job artifacts? | Open pipeline/summary.json |
phase_report_path | Did the phase timeline validate? | Open when you need coverage or ordering detail |
events_jsonl_path | Where is the full run-scoped event stream? | Use when narrower pointers are still insufficient |
snapshot_head_sha | Which exact revision ran? | Confirm you are debugging the right code |
Receipts do not inline artifact pointers or failing-step pointers. Use logs_dir to move into the runtime logs contract:
- Open the receipt.
- Check
statusandexit_code. - Follow
logs_dirtopipeline/summary.json, thenpipeline/manifest.json. - Follow job-level pointers from there.
- Use
phase_report_pathwhen you need phase validation rather than failure output.
phase_report_path
The current loom run --local receipt surfaces phase_report_path as a first-class pointer to:
.loom/.runtime/logs/<run_id>/phase-report.json
Use it when you need to answer questions like:
- Was a required phase boundary missing?
- Did the current
artifact_extractsection emit as the runtime-logs v2 compatibility phasejob.artifact_restore? - Was runtime coverage fully attributed?
phase-report.json is separate from the main failure ladder. It complements pipeline/manifest.json; it does not replace it.
Artifact navigation
Receipts point to artifacts indirectly:
- Receipt
logs_dir pipeline/manifest.json- A job entry's
artifacts_pathorartifacts_archive_path
That keeps the receipt small while still giving you a deterministic path to published job outputs. For publication diagnosis, inspect executor_receipt.Nodes[].ArtifactPublication before following the logs pointer.
Versioning and compatibility
Receipts carry schema_version: "v1".
During Alpha, Loom may add fields. Breaking changes should increment schema_version. The optional
executor_receipt.WorkflowRuleDecision field is an additive v1 change and carries its own nested version.
Privacy and sharing
Receipts contain machine-useful pointers, but many fields are sensitive enough to review before sharing outside your team.
| Field | Why it may be sensitive |
|---|---|
repo_root, workflow_path, snapshot_path, logs_dir, events_jsonl_path, phase_report_path | Reveal usernames, filesystem layout, and workspace structure |
snapshot_head_sha | Reveals commit identity |
command, executor Command output and errors | Shows exact flags, arguments, and command failure context |
executor_receipt.Nodes[].ArtifactPublication.SourceRoot, DestinationRoot, Path | Reveals execution and persistent filesystem layout |
When asking for help, sharing the receipt path or a redacted receipt is usually enough to start. Expand to specific log or event pointers only when needed.
Stability guidance
Safe to build against
schema_versionkindlogs_direvents_jsonl_pathphase_report_pathstatusexit_code
Do not hardcode
- The filename pattern alone
- The exact set of optional execution-context fields
- Whether every receipt kind carries the full run-local field set