Skip to main content

Runtime logs contract

Runtime logs are the on-disk diagnostics contract for every loom run --local invocation. The current implementation is phase-driven: event streams describe runtime boundaries with scope, phase_code, and phase_family, while summary and manifest documents give you stable pointers into the exact failing unit.

All paths below are relative to .loom/.runtime/logs/<run_id>/.

Contract versions

SurfaceCurrent versionNotes
Phase event streams and job or pipeline summaries or manifestsloom.runtime.logs.v2The main runtime contract for events.jsonl, pipeline/*.json, jobs/**/*.json
Run-level summary.jsonv1Convenience run summary with kind: "loom-run-local-logs"
phase-report.jsonv1Coverage and ordering report with kind: "loom-run-local-phase-report"

Directory layout

<run_id>/
events.jsonl # run-scoped phase event stream
summary.json # run-level summary + phase_report_path
phase-report.json # phase coverage and validation report
pipeline/
summary.json # pipeline status + exit code
manifest.json # job roster + failing job pointers
events.jsonl # pipeline-scoped phase events
jobs/
<job_id>/
summary.json # job status + exit code
manifest.json # step roster + section roster + artifacts metadata
events.jsonl # job-scoped phase events
user/
execution/
events.jsonl # execution envelope + mirrored step events
script/
01/
summary.json # step 1 status + output_lines
events.jsonl # step 1 phase events + output
02/
summary.json
events.jsonl
system/
provider/
summary.json # provider_prepare section summary
events.jsonl
cache_restore/
summary.json
events.jsonl
artifact_extract/
summary.json
events.jsonl
cleanup/
summary.json
events.jsonl
artifacts/
<relative-path>...
artifacts.tar.gz

Step directories use 1-indexed, zero-padded labels (01, 02, ...) matching workflow script order.

There is no standalone section manifest file. Section roster entries live inside jobs/<job_id>/manifest.json and point to each section summary and event stream.

Phase model

The runtime emits a small set of stable phase identities. Optional phases appear only when that work actually exists for the job.

ScopePhase codeWhat it means
runrun.bootstrapLoom captured run context and isolated workspace metadata.
runrun.pipeline_executeOuter run envelope around pipeline execution.
pipelinepipeline.executeThe pipeline body from first runnable work to pipeline result.
jobjob.provider_prepareJob start, provider setup, service startup, and similar pre-execution system work.
jobjob.cache_restoreOptional cache restore work before user execution.
jobjob.artifact_restoreThe current artifact_extract section is normalized to this phase identity.
jobjob.executionThe job's user execution envelope.
stepexecution.scriptA single script: step. Emitted per step and mirrored into broader streams.
jobjob.cache_saveOptional cache save work after execution.
jobjob.cleanupJob cleanup boundary. A finish record is still emitted when the job never ran.
runrun.finalizeFinal contract-artifact writeout after pipeline completion.

Phase family mapping

Phase familyPhase codes
orchestrationrun.*, pipeline.execute, custom non-provider system phases
providerjob.provider_prepare
cachejob.cache_restore, job.cache_save
artifactjob.artifact_restore
userjob.execution, execution.*
cleanupjob.cleanup

The current local runtime artifact section is artifact_extract, and its summaries and event streams normalize to phase_code: "job.artifact_restore" with phase_family: "artifact". Build tooling around phase_code and phase_family, not the raw directory name alone.

Event stream contract

Every events.jsonl file is newline-delimited JSON. Each line is a single event record.

Guaranteed fields on every record

FieldTypeDescription
schema_versionstringAlways loom.runtime.logs.v2
tsstringRFC 3339 timestamp with nanosecond precision
seqintegerMonotonic sequence number within that file
levelstringUsually info, warn, or error
eventstringphase_start, output, or phase_finish
run_idstringRun identifier
pipeline_idstringPipeline identifier
scopestringrun, pipeline, job, section, or step
phase_codestringStable phase identity such as job.execution
phase_familystringStable family such as user or cache

Common contextual fields

FieldWhere it appearsDescription
job_name, job_idJob, section, and step recordsJob identity
section_family, sectionSection and step recordssystem or user family; section name such as provider or execution
subphase, subphase_indexStep recordsStep subphase identity, currently script and the 1-indexed step number
step_index, step_idStep recordsPer-step identity
stream, messageoutput recordsStream (stdout or stderr) and emitted message
status, exit_code, duration_msphase_finish recordsOutcome fields
metricsphase_finish recordsFamily-specific measurements and skip metadata
skipped, skip_reasonSome system and cleanup finishesExplicit skip markers
isolated_workspace_path, isolated_workspace_sourceRun bootstrap, provider prepare, cleanupWorkspace attribution fields

Event kinds

EventRequired extra fieldsPurpose
phase_startContext fields for the scopeMarks the beginning of a phase boundary
outputstream, messageCaptured runtime output within the current phase
phase_finishstatus, duration_ms; often exit_code, metricsMarks the end of a phase boundary

Example:

{"schema_version":"loom.runtime.logs.v2","ts":"2026-03-07T12:00:03Z","seq":11,"level":"info","event":"phase_start","run_id":"loom-run-local-1772865600000000000","pipeline_id":"loom-local-1772865600000000000","scope":"job","phase_code":"job.execution","phase_family":"user","job_name":"build","job_id":"build"}
{"schema_version":"loom.runtime.logs.v2","ts":"2026-03-07T12:00:04Z","seq":12,"level":"info","event":"output","run_id":"loom-run-local-1772865600000000000","pipeline_id":"loom-local-1772865600000000000","scope":"step","phase_code":"execution.script","phase_family":"user","job_name":"build","job_id":"build","section_family":"user","section":"execution","subphase":"script","subphase_index":1,"step_index":1,"step_id":"script-01","stream":"stderr","message":"pnpm test"}
{"schema_version":"loom.runtime.logs.v2","ts":"2026-03-07T12:00:05Z","seq":13,"level":"error","event":"phase_finish","run_id":"loom-run-local-1772865600000000000","pipeline_id":"loom-local-1772865600000000000","scope":"step","phase_code":"execution.script","phase_family":"user","job_name":"build","job_id":"build","section_family":"user","section":"execution","subphase":"script","subphase_index":1,"step_index":1,"step_id":"script-01","status":"failed","exit_code":1,"duration_ms":942}

Summary and manifest documents

pipeline/summary.json

Always present. Current fields:

FieldTypeDescription
schema_versionstringloom.runtime.logs.v2
run_idstringRun identifier
pipeline_idstringPipeline identifier
statusstringsuccess or failure
exit_codeinteger0 on success, non-zero on failure
duration_msintegerPipeline duration
errorstringPresent when the pipeline surfaces an error message

pipeline/manifest.json

Always present. It is the top-level pointer document for job diagnostics.

FieldTypeDescription
schema_versionstringloom.runtime.logs.v2
run_id, pipeline_id, status, exit_code, duration_msmixedPipeline identity and outcome. Current failure token is failure.
jobsobject[]Job roster entries
failing_job_idstringPresent on failure
failing_job_manifest_pathstringPresent on failure

Each jobs[] entry currently includes:

FieldTypeDescription
job_name, job_idstringHuman-readable and filesystem-safe job identity
status, exit_code, duration_msmixedJob outcome
job_manifest_path, job_summary_pathstringRelative pointers into the job contract artifacts
system_events_pathstringProvider section events path for that job
artifacts_pathstringRelative artifacts directory pointer
artifacts_archive_pathstringPresent when artifacts.tar.gz exists

jobs/<job_id>/summary.json

Always present per job.

FieldTypeDescription
schema_versionstringloom.runtime.logs.v2
run_id, pipeline_id, job_name, job_idmixedJob identity
status, exit_code, duration_msmixedJob outcome
errorstringPresent on failure when Loom has job-level error text

jobs/<job_id>/manifest.json

Always present per job. This is the main branching document for user-step vs system-section failures.

FieldTypeDescription
schema_versionstringloom.runtime.logs.v2
run_id, pipeline_id, job_name, job_idmixedJob identity
status, exit_code, duration_msmixedJob outcome
user_stepsobject[]Step roster
system_sectionsobject[]Section roster with summary and events pointers
artifactsobjectArtifact metadata object
artifact_namestringPresent when the job declares a named artifact
failing_sectionstringPresent on failure
failing_step_indexintegerPresent on user-step failure
failing_step_events_pathstringPresent on user-step failure

Each user_steps[] entry currently includes:

FieldTypeDescription
sectionstringCurrently script
step_index, step_idmixed1-indexed step identity
command_previewstringTruncated command preview
step_summary_path, step_events_pathstringRelative pointers to the step artifacts

Each system_sections[] entry currently includes:

FieldTypeDescription
system_sectionstringRaw section name such as provider or cache_restore
section_familystringsystem
sectionstringSame logical section name
phase_code, phase_familystringNormalized phase identity
status, exit_code, duration_ms, output_linesmixedSection outcome and size
metricsobjectSection metrics payload
skipped, skip_reasonmixedPresent for explicit skipped sections
summary_path, events_pathstringRelative pointers to the section artifacts

Step summary

Path: jobs/<job_id>/user/execution/script/<NN>/summary.json

FieldTypeDescription
schema_versionstringloom.runtime.logs.v2
run_id, pipeline_id, job_name, job_idmixedStep identity context
section_familystringuser
sectionstringCurrently script
step_index, step_idmixedStep identity
status, exit_code, duration_ms, output_linesmixedStep outcome and output count

System section summary

Path: jobs/<job_id>/system/<section>/summary.json

FieldTypeDescription
schema_versionstringloom.runtime.logs.v2
run_id, pipeline_id, job_name, job_idmixedSection identity context
section_familystringsystem
sectionstringRaw section name
phase_code, phase_familystringNormalized phase identity
status, exit_code, duration_ms, output_linesmixedSection outcome and output count
metricsobjectSection metrics payload
skipped, skip_reasonmixedPresent for explicit skipped sections

Skip and metrics semantics

Skipped work is represented as structured data, not just by omission.

SurfaceHow skip appearsCurrent notes
System section phase_finish and summary.jsonstatus: "skipped" plus metrics.skipped: true; often top-level skip_reason and metrics.skip_reason tooUsed for cache and artifact no-op or policy-driven skips
Job manifest system_sections[]Mirrors the same status, skipped, skip_reason, and metrics fieldsUse this for pointer-first triage without opening the section summary first
job.cleanup finish when a job never ranstatus: "skipped" with skip metricsThis is the reliable skipped/not-run job boundary today
Step summariesUnstarted steps resolve to status: "skipped"No extra skip object is guaranteed today

Current metric families:

Phase familyCommon metric keys
cacheskipped, skip_reason, cache_name, cache_key, scope_hash, key_hash, hit
artifactskipped, skip_reason, file_count, archive_format, archive_path, archive_size_bytes
user, cleanup, orchestrationEmpty object or family-specific additions

Unknown metric keys are allowed. Do not hardcode the full metric set.

Job artifacts

When Loom extracts job artifacts, it writes them under:

jobs/<job_id>/artifacts/

When an archive is produced, it is written at:

jobs/<job_id>/artifacts/artifacts.tar.gz

Artifact metadata

jobs/<job_id>/manifest.json currently includes an artifacts object for the job:

FieldTypeDescription
base_pathstringRelative artifacts directory
file_countintegerNumber of extracted files
total_bytesintegerTotal extracted file bytes
filesobject[]Per-file entries with path and size_bytes
archive_pathstringPresent when artifacts.tar.gz exists
archive_formatstringCurrently tar.gz when an archive exists
archive_size_bytesintegerArchive byte size when present

The pipeline manifest mirrors top-level artifact pointers through artifacts_path and artifacts_archive_path.

phase-report.json

phase-report.json is a run-level validation artifact written beside the logs, not inside pipeline/ or a job directory.

FieldTypeDescription
schema_versionstringv1
kindstringloom-run-local-phase-report
run_idintegerNumeric run id used to build the log directory name
events_jsonl_pathstringAbsolute path to the run-scoped event stream
started_at, finished_at, duration_msmixedRun timing
status, exit_code, errormixedRun outcome
planobjectRequired phase instances Loom expected to see
validationobjectWhether the observed timeline satisfied the contract
coverageobjectAttributed runtime and required-phase coverage
phase_metricsobject[]Aggregated duration and completion counts by scope and phase code

The report is useful when failure evidence is not enough and you need to answer questions like:

  • Did a required phase boundary never emit?
  • Did the current artifact extraction phase emit?
  • How much runtime was attributed to cache, artifact, or execution phases?

Loom writes phase-report.json before surfacing validation failure, so the artifact remains available even when the phase timeline is invalid.

Pointer-first triage

Use this order for the fewest file reads:

StepFileWhat to look for
1pipeline/summary.jsonstatus, exit_code, and top-level pipeline error
2pipeline/manifest.jsonfailing_job_id and failing_job_manifest_path
3jobs/<job_id>/manifest.jsonfailing_step_events_path or the relevant system_sections[].events_path
4The pointed events.jsonloutput lines and the closing phase_finish
5phase-report.jsonOptional: coverage, missing boundaries, and ordering issues

Open one failing job first. Widen scope only after confirming the problem spans multiple jobs or a global run boundary.

Stability guidance

Safe to build against

  • Pointer fields such as failing_job_id, failing_job_manifest_path, failing_step_events_path, summary_path, and events_path
  • Phase identity fields: scope, phase_code, phase_family
  • Outcome fields on phase_finish: status, duration_ms, and usually exit_code
  • The presence of phase-report.json, pipeline/summary.json, pipeline/manifest.json, and per-job summary.json plus manifest.json

Do not hardcode

  • The full set of raw system section directory names
  • Exact output message text
  • The complete metric-key vocabulary inside metrics
  • Whether every optional phase family appears for every job