Skip to main content

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

FieldTypeDescription
schema_versionstringAlways v1
kindstringCurrently loom-run-local for local runs
commandstring[]Exact CLI invocation
repo_rootstringAbsolute repository root
workflow_pathstringAbsolute workflow path
started_at, finished_atstringRFC 3339 timestamps
duration_msintegerWall-clock runtime in milliseconds
statusstringCurrent values are success or failure
exit_codeintegerProcess exit code
errorstringPresent when Loom surfaces a run error

Runtime pointer fields

FieldTypeDescription
logs_dirstringAbsolute path to .loom/.runtime/logs/<run_id>/
events_jsonl_pathstringAbsolute path to the run-scoped events.jsonl
phase_report_pathstringAbsolute path to phase-report.json for the run

Execution context fields

FieldTypeDescription
snapshot_pathstringSnapshot or isolated workspace path when present
snapshot_head_shastringGit revision used for the snapshot when present
graph_irobjectResolved graph IR when available
executor_receiptobjectExecutor order and per-node outcomes when available
warningsobject[]Validation or runtime warnings when present
cache_diffobjectCache-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:

FieldTypeDescription
schema_versionstringAlways loom.workflow-rule-decision.v1
outcomestringallow, deny, or evaluation_error
reasonstringStable category: rule_matched, no_rules, no_matching_rule, or evaluation_failed
rule_indexintegerZero-based decisive/failing rule, or -1 when no individual rule applies
diagnostic_codestringPresent only for a typed evaluation failure

The state mapping is deterministic:

OutcomeReasonRule index
allowrule_matchedMatched rule index
allowno_rules-1
denyno_matching_rule-1
evaluation_errorevaluation_failedFailing 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:

FieldDescription
DeclaredTargetTarget retained in resolved Graph IR. This is a declaration, not an observation.
SelectedProviderProvider selected for the node (host or docker). Selection does not prove runtime OS or architecture.
ObservedRuntimePresent only after the provider reports an observation from a started execution context. See the identity contract below.
CommandCommand/provider status, exit code, and error. A successful command remains success when later artifact publication fails.
ArtifactPublicationPresent 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:

FieldDescription
OSObserved operating-system identifier. Empty or absent means unknown.
ArchitectureHost: Go architecture of the executing Loom process. Docker: OCI architecture reported for the exact image used by the started container.
Sourcehost_process or docker_container_image.
UnavailableReasonSafe 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:

FieldWhat it answersNext step
status, exit_codeDid the run succeed?If not, open logs_dir
logs_dirWhere are the pipeline and job artifacts?Open pipeline/summary.json
phase_report_pathDid the phase timeline validate?Open when you need coverage or ordering detail
events_jsonl_pathWhere is the full run-scoped event stream?Use when narrower pointers are still insufficient
snapshot_head_shaWhich 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:

  1. Open the receipt.
  2. Check status and exit_code.
  3. Follow logs_dir to pipeline/summary.json, then pipeline/manifest.json.
  4. Follow job-level pointers from there.
  5. Use phase_report_path when 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_extract section emit as the runtime-logs v2 compatibility phase job.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:

  1. Receipt logs_dir
  2. pipeline/manifest.json
  3. A job entry's artifacts_path or artifacts_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.

FieldWhy it may be sensitive
repo_root, workflow_path, snapshot_path, logs_dir, events_jsonl_path, phase_report_pathReveal usernames, filesystem layout, and workspace structure
snapshot_head_shaReveals commit identity
command, executor Command output and errorsShows exact flags, arguments, and command failure context
executor_receipt.Nodes[].ArtifactPublication.SourceRoot, DestinationRoot, PathReveals 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_version
  • kind
  • logs_dir
  • events_jsonl_path
  • phase_report_path
  • status
  • exit_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