Receipts contract
Current loom run records workflow state and events in the
two-file runtime logs format. It writes no run receipt file.
CLI and MCP readers use state.jsonl and events.jsonl directly.
loom check continues to write validation receipts under .loom/.runtime/receipts/.
The sections below document historical run receipts and native captures retained
for saved-upload workflows. Current run readers do not interpret these formats.
Historical receipt schema (v1)
The receipt is JSON. The field list below describes the historical 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 |
duration_us | integer | Same span in microseconds (loom-check) |
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 | Native logical pointer to the run's log placement; not a promise of sidecar files |
events_jsonl_path | string | Native logical pointer to run-scoped events, read with the events_jsonl selector |
phase_report_path | string | Native phase-report pointer; reader projection selects the UUID original |
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".
A provider that fails before producing any phase evidence (for example a Docker workspace seed error or an unavailable Docker daemon) records Command.Status: "not_run" with Command.Error set to provider failed before command execution:, a space, and then the redacted provider error. The node Error reports that provider error alone rather than the internal phase-evidence validation text, and a non-zero provider exit code (for example 125 for an unavailable daemon) is kept as the node ExitCode instead of the generic 1. Malformed phase evidence that arrives together with a provider error keeps the validation text in Command.Error and lists the provider error first in the node Error.
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