Skip to content

Runs

Start one run with run, follow it with watch, and read the result with status, checks, receipt, diff and verify.

A run moves through seven states: queued, running, awaiting_confirmation, awaiting_sample_approval, succeeded, failed and cancelled. It stops in three of them, succeeded, failed and cancelled. There is no released state. A succeeded run’s version is a workspace candidate the moment it exists. The first succeeded run of a table makes that table live.

Every failed run carries a failure triple: failure_code, failure_detail and failed_stage. The triple sits on the run record, in the terminal event and in every refusal built from it. This client refuses a record whose triple is incomplete.

Terminal window
mr-data run --recipe RECIPE_ID --digest HEX (--sample | --full | --refresh | --backfill | --mode MODE)
[--max-rows N] [--max-source-bytes N] [--window START END]
[--sources-from RUN_ID] [--resource-class CLASS] [--confirm]
[--no-activity] [--json]
mr-data run --cancel RUN_ID [--json]

mr-data run starts one run against a registered recipe under stated ceilings. The digest names which bytes of the recipe to run. This client states it and Studio checks it, never derives it. A digest that does not match the registered recipe refuses the run.

Start with --full. Full builds use progressive acquisition by default: an inspection checkpoint becomes available after about five minutes, then the same acquisition continues automatically. It needs no second approval or full run. Over the projection threshold Studio holds the run for confirmation before acquisition. Use --sample with a ceiling only for a deliberately bounded experiment; row limits do not bound acquisition time.

The client settles every argument before it resolves a credential. A command line it cannot accept never reaches the network.

Flag Argument Required Meaning Default
--recipe RECIPE_ID yes The registered recipe to run. none
--digest HEX yes The recipe digest those bytes carry, bare lowercase hex. Checked, never derived. none
--mode sample|full|refresh|backfill|compact|replay one of these or a shorthand The run mode to request. none
--sample none see --mode Shorthand for --mode sample. Run an explicit bounded experiment with a ceiling; row limits do not limit acquisition time. none
--full none see --mode Shorthand for --mode full. Full builds use progressive acquisition automatically. Over the projection threshold it is held for confirmation first. none
--refresh none see --mode Shorthand for --mode refresh. Run forward from where the last successful run reached. none
--backfill none see --mode Shorthand for --mode backfill. Run one exact window, stated with --window. none
--max-rows N no Stop at this many rows from each source. A recipe with eight sources can deliver eight times this many rows. server ceiling
--max-source-bytes N no Stop before fetching more than this from the sources. The run fails in every mode if it exceeds this limit because bytes already transferred cannot be truncated. server ceiling
--window START END with --backfill The half-open time range to cover. The start is included and the end is not. none
--sources-from RUN_ID with --mode replay Replay the retained raw inputs of this successful run. A replay never becomes live. Refused on any other mode. none
--resource-class CLASS no The capacity class to ask for. Studio refuses one it cannot satisfy. server default
--confirm none no Authorize the run if it is held for a confirmation, instead of printing the projection. off
--cancel RUN_ID no Cancel this run instead of starting one. Legal while it is queued, held or running. none
--no-activity none no Do not tell the dataset page what this command is doing. off
--json none no One JSON object instead of human lines. off

--sample with no ceiling at all is refused. A sample with no ceiling is a full run, dispatched and billed as one.

mr-data run --recipe … --digest … --sample --max-rows 500 --json
{
"schema_version": "mostlyright-thin-client-v4-run.v1",
"status": "run_queued",
"lane": "hosted",
"dashboard_url": "https://mostlyright.md/datasets/d31017e0/runs/7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"watch_command": "mr-data watch 7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"run": {
"run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"workspace_id": "39ffafde-…",
"recipe_id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
"recipe_digest": "9f3c…",
"dataset_id": "d31017e0",
"table_id": "3f8c2d10-6a4b-4c8e-9d1f-5b7a0c2e4d68",
"mode": "sample",
"status": "queued",
"clamps": { "max_rows": 500 },
"resource_class": null,
"created_at": "2026-09-12T09:00:00Z",
"completed_at": null,
"coverage": null,
"table_version_id": null,
"candidate_digest": null,
"sample_run_id": null,
"full_run_id": null
}
}

Values are illustrative. status is run_queued when the returned run is queued, run_returned when submission returns a run already in another state, run_cancelled under --cancel, and run_confirmed when --confirm settled the confirmation gate. The run_confirmed payload also carries confirmed_from_status and the three projection numbers.

held at the confirmation gate (exit 2)
{
"schema_version": "mostlyright-thin-client-v4-run.v1",
"status": "run_awaiting_confirmation",
"lane": "hosted",
"run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"projected_bytes": 41000000000,
"projected_runtime_s": 3600,
"projected_cost": "12.40",
"confirm_command": "mr-data run --recipe 1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d --digest 9f3c… --mode full --confirm"
}

The run already exists, persisted awaiting_confirmation. confirm_command re-states the request that was sent, always spelled --mode MODE for every mode. --confirm authorizes the projection on it. This payload exits 2.

Errors:

Code When
THIN_REQUEST_INVALID No --recipe. No --digest. No mode. A --sample with no ceiling. A --backfill with no --window.
THIN_ARGUMENT_INVALID --mode replay with no --sources-from. --sources-from on a mode that acquires its own inputs. A --sources-from that is not a run identifier.
THIN_STUDIO_CONFIRM_REQUIRED Studio raises it and this client handles it. Without --confirm it becomes the run_awaiting_confirmation payload.
THIN_RESPONSE_INVALID Studio held the run for a confirmation without saying what it projected.
THIN_STUDIO_* A digest that does not match the registered recipe, a resource class Studio cannot satisfy, a cancel of a run that already finished, and the rest of Studio’s refusals.
one full build, followed and verified
mr-data run --recipe "$RECIPE_ID" --digest "$DIGEST" --full --json
mr-data watch "$RUN_ID"
mr-data checks "$RUN_ID" --json
Terminal window
mr-data runs [--status STATUS] [--mode MODE] [--limit N] [--json]

mr-data runs reports this workspace’s own runs, newest first, narrowed by status and by mode. Use it to find a run identifier, or to see what has been happening.

Flag Argument Required Meaning Default
--status one of seven no queued, running, awaiting_confirmation, awaiting_sample_approval, succeeded, failed, cancelled. A word outside this list is refused before the network. every status
--mode one of six no sample, full, refresh, backfill, compact, replay. every mode
--limit N no How many runs to report. The command follows the workspace’s own pages until it has that many or there are no more, up to 200 pages. 20
--json none no One JSON object instead of human lines. off
mr-data runs --status failed --json
{
"schema_version": "mostlyright-thin-client-v4-runs.v1",
"status": "runs_listed",
"lane": "hosted",
"count": 2,
"runs": {
"run 1": { "run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90", "mode": "refresh", "status": "failed", "created_at": "2026-09-12T08:00:00Z",
"failure": { "failure_code": "CONTENT_LENGTH", "failure_detail": "…", "failed_stage": "acquire" } }
},
"next_cursor": null,
"filters": { "status": "failed", "mode": null }
}

Values are illustrative. Every listed row carries those four members. A row carries failure, the nested triple, only on a run that failed. It carries pages, one sentence about page coverage, only on a run whose record holds a page-collection block.

Errors: THIN_ARGUMENT_INVALID for a --status or --mode naming a word this contract does not have.

Terminal window
mr-data runs --limit 5 --json | jq -r '.runs[] | "\(.run_id) \(.mode) \(.status)"'
Terminal window
mr-data status RUN_ID [--receipts] [--json]

mr-data status reports which of the seven states one run is in, what it delivered, and why it failed. Studio refuses a run in another workspace the same way it refuses one that does not exist.

Flag Argument Required Meaning Default
RUN_ID id yes The run to report, by the identifier the start printed. none
--receipts none no No effect here. The run record this command reads already carries the digests, sources, timings, checks and causes. The payload reports the flag under flags_without_effect. off
--json none no One JSON object instead of human lines. off
mr-data status RUN_ID --json
{
"schema_version": "mostlyright-thin-client-v4-run-status.v1",
"status": "run_status_reported",
"lane": "hosted",
"terminal": true,
"run": { "run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90", "mode": "full", "status": "failed", "failed_stage": "acquire", "": "…" },
"pages": "…",
"failure": { "failure_code": "CONTENT_LENGTH", "failure_detail": "…", "failed_stage": "acquire" },
"refusal": { "code": "CONTENT_LENGTH", "detail": "…" }
}

Values are illustrative. run includes the full run record projected onto the same members mr-data run prints. failure and refusal appear only on a failed run. projection appears only on one held at the confirmation gate. pages is a sentence about page coverage when the run has one. The command exits 2 when run.status is failed.

Errors: THIN_ARGUMENT_INVALID for a positional that is not a run identifier. THIN_NOT_FOUND. THIN_RESPONSE_INVALID when a failed run’s triple is incomplete.

Terminal window
mr-data status "$RUN_ID" --json | jq '{state: .run.status, why: .failure}'
Terminal window
mr-data watch RUN_ID [--from-seq N] [--last-event-id CURSOR] [--json]

mr-data watch follows one run’s live progress and prints what it stopped on. The stream resumes across cuts on its own. --last-event-id resumes a watch that ended on this side. The run keeps going when nothing is watching it.

Flag Argument Required Meaning Default
RUN_ID id yes The run to follow, by the identifier the start printed. none
--from-seq N no Replay from after this log position. Ignored once a cursor is known. none
--last-event-id CURSOR no Resume from this cursor, as a previous watch reported it. none
--json none no One JSON object instead of human lines. off
mr-data watch RUN_ID --json
{
"schema_version": "mostlyright-thin-client-v4-watch.v1",
"status": "run_terminal",
"lane": "hosted",
"run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"dashboard_url": "https://mostlyright.md/datasets/d31017e0/runs/7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"terminal_event": "run_succeeded",
"stream": {
"last_event_id": "88-…",
"last_sequence": 88,
"events_seen": 88,
"progress_seen": 41,
"stage": "persist",
"stage_index": 5,
"stage_total": 5,
"status": "succeeded",
"end_reason": "run_succeeded",
"reconnects": 1,
"halted": false,
"expired": false,
"failure": null,
"diagnostic": null
}
}

Values are illustrative. terminal_event is run_succeeded, run_failed or null. On run_failed the payload also includes failure and refusal built from the same triple, and the command exits 2. Progress events are narration. Nothing in stream is evidence about the build.

Errors: THIN_STREAM_UNAVAILABLE, THIN_STREAM_FRAME_INVALID, THIN_STREAM_LINE_TOO_LONG, THIN_STREAM_REFUSED, THIN_NOT_FOUND.

Terminal window
mr-data watch "$RUN_ID" || echo "the run failed; mr-data status $RUN_ID says why"
Terminal window
mr-data checks RUN_ID [--no-activity] [--json]

mr-data checks reports how each check the recipe declared came out for one run. It exits 2 when any declared check failed.

The command reads the run record first, to learn whether the run has finished. It reads no version, no promotion and no approval.

Flag Argument Required Meaning Default
RUN_ID id yes The run to read. none
--no-activity none no Do not tell the dataset page what this command is doing. off
--json none no One JSON object instead of human lines. off
mr-data checks RUN_ID --json
{
"schema_version": "mostlyright-thin-client-v4-checks.v1",
"status": "checks_reported",
"lane": "hosted",
"run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"dashboard_url": "https://mostlyright.md/datasets/d31017e0/runs/7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"run_status": "succeeded",
"checks_scope": "version",
"checks": {
"check 1": { "check_id": "max_temp_within_range", "passed": true, "detail": null, "scope": "version" }
},
"check_count": 4,
"checks_passed": 4,
"checks_failed": 0
}

Values are illustrative. checks_scope comes off the run’s own receipt, and is null when that receipt could not be read. On any scope but version, the payload carries a note. The checks ran over the rows this run added, not over every row of the version it composed.

On a failed run the payload includes empty checks, zero counts and the failure and refusal blocks. A required check that fails terminalizes the run, so the failure names the failed check. The command exits 2 when checks_failed is non-zero or the run failed.

Errors: THIN_NO_CHECK_RESULTS in three cases. The run has not finished, and the message names the state and points at mr-data watch. The run was cancelled before its checks ran. Studio has the run as succeeded and its log carries no terminal record.

Terminal window
mr-data checks "$RUN_ID" --json && echo "safe to publish"
Terminal window
mr-data receipt RUN_ID [--no-activity] [--json]

mr-data receipt prints the receipt, the record of how one run was built: the sources it fetched, their digests, the timings, the checks and the causes. The document arrives verbatim under receipt.

Flag Argument Required Meaning Default
RUN_ID id yes The run to read. none
--no-activity none no Do not tell the dataset page what this command is doing. off
--json none no One JSON object instead of human lines. off
mr-data receipt RUN_ID --json
{
"schema_version": "mostlyright-thin-client-v4-receipt.v1",
"status": "receipt_reported",
"lane": "hosted",
"run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"dashboard_url": "https://mostlyright.md/datasets",
"artifact_id": "5e2a9c74-1d6b-4308-b9c5-8f0a3d7e2b41",
"digest": "9f3c…",
"size_bytes": 18422,
"receipt": {}
}

Studio caps a run at one artifact per kind per attempt. A run that never retried wrote one receipt. A run that retried wrote one per attempt, and this command reads the first listed. artifact_id says which one.

Errors: THIN_NO_MATCHING_ARTIFACT when the run wrote no receipt. THIN_ARTIFACT_TOO_LARGE, THIN_NOT_FOUND.

Terminal window
mr-data receipt "$RUN_ID" --json | jq '.receipt'
Terminal window
mr-data note --run RUN [--heading TEXT] [--cell-id ID] [--markdown-file FILE]
[--blocks-file FILE] [--phase {acquire,parse,transform,check,persist}]
[--checkpoint N] [--revise CELL_ID] [--list] [--json]

mr-data note writes one cell of the decision record into a run’s log while the run is going. A cell is one heading and a markdown body, with typed blocks beside it. The dataset page lays out the blocks.

Writing the same cell identifier again revises that cell in place, and the superseded version stays in the log. A run’s log closes when the run reaches a terminal state. Research a person will read next week goes on the dataset instead.

A cell is not sealed. No receipt quotes it, and it is not evidence about the build.

Flag Argument Required Meaning Default
--run RUN yes The run this cell is about. none
--list none no List the cells this run holds instead of writing one, superseded ones flagged. off
--heading TEXT to write The cell’s one outcome-led line. At most 200 characters. none
--cell-id ID no A stable identifier. Writing it again revises the cell in place. Letters, digits, dot, dash and underscore, opening on a letter or digit, at most 64 characters. derived from the heading
--markdown-file FILE no The cell body as UTF-8 markdown. At most 65536 bytes. standard input
--blocks-file FILE no The cell’s typed blocks as a JSON array of at most 64 source, decision or clarification objects. none
--phase one of five no acquire, parse, transform, check, persist, the worker protocol’s own stages, so the cell lands in a bucket the log already has. none
--checkpoint N or cursor no The run-event sequence this cell is about, as the number or as the cursor mr-data watch printed beside it. the cell’s own append sequence
--revise CELL_ID no The identifier of a different cell this one supersedes. Revising a cell under its own identifier needs no flag. none
--json none no One JSON object instead of human lines. off

Block fields, by kind:

Kind Required Optional
source name, provides, status (using, considered or rejected) host, grain, coverage, reason
decision chose, because over
clarification question answer, answered_at (only beside an answer)

Labels are at most 200 characters, hosts 255, text 1024. A key the kind does not declare refuses rather than going unread.

mr-data note --run RUN --heading '…' --json
{
"schema_version": "mostlyright-thin-client-v4-narrative.v1",
"status": "narrative_cell_appended",
"lane": "hosted",
"run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"workspace_id": "39ffafde-…",
"cell": {
"cell_id": "dropped-station-kbos",
"heading": "Dropped KBOS: the feed stopped in 2023",
"checkpoint_seq": 41,
"revision_of": null,
"sequence": 3,
"cursor": "3-…",
"appended_at": "2026-09-12T09:00:00Z",
"payload_digest": "9f3c…",
"superseded": null,
"markdown_bytes": 420,
"block_count": 1,
"phase": "acquire"
},
"sealed": false,
"not_evidence": "…",
"flags_without_effect": {}
}

--list answers status: "narrative_listed" with cell_count, rendered_count, source_truncated and cells.

Errors: THIN_NARRATIVE_CELL_TOO_LARGE, THIN_NARRATIVE_CELL_CONTROL_CHARACTER and THIN_NARRATIVE_REVISION_INVALID. THIN_REQUEST_INVALID for a bad --cell-id, a --blocks-file over its ceiling or holding more than 64 blocks, or a block field the kind does not declare. THIN_RESPONSE_INVALID when Studio does not describe the cell as unsealed.

record why a source was dropped
mr-data note --run "$RUN_ID" \
--heading "Dropped KBOS: the feed stopped in 2023" \
--phase acquire \
--blocks-file blocks.json < body.md
Terminal window
mr-data diff LEFT RIGHT [--columns-only] [--json]

mr-data diff compares two runs and says what changed. Without --columns-only it compares the row count, the window covered and the data’s own fingerprint as well as the columns. A run that failed at acquire wrote no column profile. That side reads as unknown.

Flag Argument Required Meaning Default
LEFT run id yes The first run. none
RIGHT run id yes The second run. none
--columns-only none no Compare the columns alone, out of each run’s column profile. off
--json none no One JSON object instead of human lines. off
mr-data diff RUN_A RUN_B --json
{
"schema_version": "mostlyright-thin-client-v4-comparison.v1",
"status": "builds_differ",
"lane": "hosted",
"summary": "…",
"change count": 2,
"changes": { "change 1": "…", "change 2": "…" },
"first run": { "run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90", "row_count": 4100000, "columns": {}, "columns_known": true, "column_profile": "5e2a9c74-1d6b-4308-b9c5-8f0a3d7e2b41", "candidate_digest": "9f3c…" },
"second run": { "run_id": "run_02J…", "row_count": 4210000, "columns": {}, "columns_known": true, "column_profile": "art_02J…", "candidate_digest": "a1b2…" },
"columns not compared": [],
"columns only": false
}

Values are illustrative. status is builds_identical or builds_differ. columns not compared appears only when a side wrote no column profile. columns only appears only under the flag.

Errors: THIN_NO_FINGERPRINT, THIN_NOT_FOUND, THIN_ARGUMENT_INVALID.

Terminal window
mr-data diff "$YESTERDAY_RUN" "$TODAY_RUN" --json | jq '.summary, .changes'
Terminal window
mr-data verify RUN_ID [--deep] [--json]

mr-data verify holds this run’s table manifest against the parts it names. Without --deep it fetches no part, so a clean answer does not prove every part’s bytes still match the manifest. With --deep it fetches every part this run wrote and re-hashes it against the digest the manifest states.

Flag Argument Required Meaning Default
RUN_ID id yes The run to read. none
--deep none no Fetch every part and re-hash its bytes. off
--json none no One JSON object instead of human lines. off
mr-data verify RUN_ID --deep --json
{
"schema_version": "mostlyright-thin-client-v4-table-verification.v1",
"lane": "hosted",
"run_id": "7c9e1d34-8b52-4f61-9a03-2e5d6f7a8b90",
"dashboard_url": "https://mostlyright.md/datasets",
"deep": true,
"status": "table_verified",
"layout_format_version": 2,
"manifest_digest": "9f3c…",
"part_count": 128,
"row_count": 4100000,
"total_bytes": 811000000,
"parts_read": 128,
"findings": []
}

Values are illustrative. status is table_verified or table_disagrees. findings names each disagreement, and a non-empty findings exits 2. parts_read is 0 without --deep.

Errors: THIN_VERSION_PART_UNREACHABLE, THIN_NO_SEALED_RESULT, THIN_RESULT_DIGEST_MISMATCH, THIN_NOT_FOUND.

Terminal window
mr-data verify "$RUN_ID" --deep --json | jq '{status, part_count, findings}'

Identical submissions are idempotent. A returned failed or completed run is not a new attempt. Read run.status; run_returned does not mean queued. Failed runs return exit code 2 with the recorded failure. For recognized execution failures, the CLI supplies the existing --retry command. A deterministic source or recipe failure needs a corrected recipe.