# Datasets

Source: https://mostlyright.md/docs/reference/cli/dataset/

Create a dataset page, update its record, choose who can read it and add a cover image.

## dataset

```bash
mr-data dataset {categories,create,set,draft,show,state,note,activity,watch,publish,archive} [DATASET_ID] [options] [--json]
```

`mr-data dataset` is one name over eleven verbs. They all act on the page a person is looking at.

Create the page before the research starts, so whoever is watching sees it fill in rather than appear finished. Every other verb edits a page that already exists.

The decision record here belongs to the dataset. A run's own record closes when the run does. Research a person will read next week goes on the dataset, and what happened inside one run goes on [that run](/docs/reference/cli/run/).

### Verbs

| Verb | Needs `DATASET_ID` | What it does |
| --- | --- | --- |
| `categories` | no | Print the fixed primary-category vocabulary and its assignment policy. Reaches no network. |
| `create` | no | Mint the page from a working title. This is what produces a dataset identifier. From 0.28.0, when the dataset you created on mostlyright.md for your request is still empty and open, it answers `open_request_reused` with that dataset instead of minting a second one. |
| `show` | yes | Read the page back as Studio holds it. |
| `set` | yes | Write the title, topics, licence or description, conditional on the version read. |
| `draft` | yes | Write a bounded live description snapshot without changing the saved description. |
| `state` | yes | Read the bounded current state, plus paged source inventory, recording membership and event history. |
| `note` | yes | Write one cell of the dataset's decision record, or `--list` what is written. |
| `activity` | yes | Say in one line what is happening right now. The page shows it, then forgets it. |
| `watch` | yes | Follow the page's own event stream. |
| `publish` | yes | Say who can read the dataset, or `--show` who can now. |
| `archive` | yes | Retire the page and free its title. Nothing built is deleted. |

### Options

| Flag | Argument | Required | Meaning | Default |
| --- | --- | --- | --- | --- |
| `--json` | none | no | One JSON object instead of human lines. | off |
| `--name` | `NAME` | `create` | With `create`, the working title the page opens under. With `set`, the title it settles on. `set` requires any one of `--name`, `--category`, `--topics`, `--license` and `--description-file`. At most 160 characters. | none |
| `--expected-version` | `N` | `draft` | With `draft` or `set`, preserve this dataset version and refuse intervening edits. `draft` requires it, from `create`, `show` or the previous draft. | `set` reads the current version itself |
| `--handoff` | none | no | With `create`, also prepare the single-use address the host opens immediately. | off |
| `--separate` | none | no | With `create`, mint a new dataset even while the one you created for your request is waiting. Use it only for a genuinely different dataset. | off |
| `--description-file` | `FILE` | `draft` | The description as UTF-8 markdown read from a file. `-` reads standard input. At most 4000 characters, the same bound the recipe document's `dataset.description` carries. | none |
| `--category` | one id | no | With `set`, the primary category, from the vocabulary this page lists. | none |
| `--topics` | `a,b,c` | no | With `set`, descriptive tags as one comma-separated list, separate from the primary category. This client trims and lowercases them. Studio holds the bounds: at most eight, no duplicates, each 1 to 40 characters of lowercase letters, digits, spaces and hyphens. `--topics ''` takes them off the page. | none |
| `--license` | `ID` | no | With `set`, the SPDX identifier of the rights basis the sources grant, such as `CC-BY-4.0` or `ODbL-1.0`. `--license ''` takes it off. Leave it off where the sources' terms are not an open licence. | none |
| `--confirm-name` | `TITLE` | `archive` | With `archive`, the dataset's current title, typed out. | none |
| `--mode` | `public\|link\|private` | no | With `publish`, who can read this dataset. | `public` |
| `--show` | none | no | With `publish`, report who can read the dataset now and change nothing. | off |
| `--heading` | `TEXT` | `note` | With `note`, the cell's one outcome-led line. The page shows it. | none |
| `--cell-id` | `ID` | no | With `note`, a stable identifier. Writing it again revises the cell in place. | derived from the heading |
| `--markdown-file` | `FILE` | no | With `note`, the cell body as UTF-8 markdown. | standard input |
| `--blocks-file` | `FILE` | no | With `note`, the cell's typed blocks as a JSON array of `source`, `decision` or `clarification` objects. A clarification carrying a question and no answer is the pending question the page shows and the chat asks. | none |
| `--phase` | word | no | With `note`, one of `acquire`, `parse`, `transform`, `check`, `persist`. With `activity`, one of `researching`, `comparing`, `drafting`, `building`, `verifying`, `waiting_on_you`, `done`. The two vocabularies are disjoint. The flag accepts the union because argparse holds one list per flag, and each verb then closes its own set, so `activity --phase acquire` is refused. | none |
| `--message` | `TEXT` | `activity` | With `activity`, the one line the page shows beside the phase word. At most 80 characters. | none |
| `--fact` | `KEY=VALUE` | no | With `activity`, one counter beside the sentence, repeatable. Keys: `sources_found`, `sources_probed`, `candidates`, `table_columns`, `checks_declared`, `rows`. Every value is a non-negative whole number. | none |
| `--ttl` | `SECONDS` | no | With `activity`, how long this stays current before the page calls it stale. 30 to 1800. | `120` |
| `--revise` | `CELL_ID` | no | With `note`, the identifier of a different cell this one supersedes, which records the answer to a pending question. Revising a cell under its own identifier needs no flag. | none |
| `--list` | none | no | With `note`, list the cells this dataset holds instead of writing one. | off |
| `--from-seq` | `N` | no | With `watch`, replay from after this log position. Ignored once a cursor is known. | none |
| `--last-event-id` | `CURSOR` | no | With `watch`, resume from this cursor, as a previous watch reported it. | none |
| `--paged-sources` | none | no | With `state`, request one paged source-state snapshot. | off |
| `--source-inventory-recipe-manifest` | `MANIFEST` | no | With `state`, the exact inventory manifest from a prior paged response. Give it with the position. | none |
| `--source-inventory-position` | `POSITION` | no | With `state`, bounded source-inventory page position. Give it with the manifest. | none |
| `--membership-position` | `POSITION` | no | With `state`, recording-membership page position. | none |
| `--membership` | `FENCE` | no | With `state`, recording-membership fence from the preceding page. | none |
| `--history-before-cursor` | `CURSOR` | no | With `state`, exclusive durable-event cursor from the preceding page. | none |
| `--history-limit` | `N` | no | With `state`, durable-event page size, 1 to 200. | server default |

A flag belonging to another writing verb returns `THIN_REQUEST_INVALID` and names the verb it belongs to. `--confirm-name` typed at `note` and `--message` typed at `set` are two examples. The client never ignores an unsupported flag.

### JSON shapes

```json title="mr-data dataset create --name '…' --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-created.v1",
  "status": "dataset_created",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "name": "Kalshi hourly BTC",
  "version": 1,
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0",
  "navigation": {
    "url": "https://mostlyright.md/datasets/d31017e0",
    "target": "/datasets/d31017e0",
    "open_before_research": true,
    "follow_agent": true,
    "status": "dashboard_ready"
  },
  "note": "The page exists now and is empty. …"
}
```

Values are illustrative. With `--handoff`, `navigation.url` becomes a single-use redeem address and `navigation.status` becomes `handoff_ready`, or `handoff_unavailable` when minting failed. The dataset was created either way. Never recreate it to retry its handoff.

From 0.28.0, `create` first asks Cloud whether you already created a dataset for your request on mostlyright.md. If that dataset is still empty and it is your only open request from the last day, nothing is minted: the receipt has `status: "open_request_reused"`, `reused_open_request: true`, that dataset's `dataset_id` and `dashboard_url`, and `navigation.open_before_research: false`, because its page is already open. Build into that `dataset_id`. The `--name` and description you passed are not applied; set them with `dataset set`. `--separate` skips the question. If Cloud cannot answer, `create` mints a dataset as before.

```json title="mr-data dataset show DATASET_ID --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-read.v1",
  "status": "dataset_reported",
  "lane": "hosted",
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0",
  "dataset_id": "d31017e0",
  "name": "Kalshi hourly BTC",
  "description": "…",
  "topics": ["bitcoin", "prediction markets"],
  "license": "CC-BY-4.0",
  "category_id": "crypto",
  "dataset_status": "active",
  "version": 7,
  "cover": { "cover_image_url": "https://…" },
  "tables": [],
  "latest_run": {},
  "activity": null
}
```

This payload carries the record's own status as `dataset_status`, because the envelope's `status` is the command's word. `activity` is `null` for a dataset nothing is working on.

```json title="mr-data dataset set … --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-updated.v1",
  "status": "dataset_updated",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "version": 8,
  "fields": ["description", "license", "name", "topics"],
  "retried_after_conflict": false,
  "idempotent_replay": false,
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0"
}
```

`retried_after_conflict` says the first conditional write lost a race and ran again at the new version. `idempotent_replay` says Studio changed nothing because the record already held those values. `version` alone cannot separate a write from a repeat. `fields` lists what this command sent.

Send `--name`, `--topics` and `--description-file` in one `set`. A description sent on its own can make Studio write a new title and new topics from it. From 0.27.1 the command reads the dataset back after a write: `changed` lists every field that moved, and `written_by_studio` names each one the command did not send, with its new value. When somebody else wrote the dataset in between, the receipt says so and attributes nothing. `draft` answers the same schema with `status: "description_drafting"` and only `dataset_id` and `version`.

```json title="mr-data dataset publish DATASET_ID --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-access.v1",
  "status": "dataset_access_changed",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "requested_mode": "public",
  "external_mode": "public",
  "public_url": "https://mostlyright.md/data/kalshi-hourly-btc",
  "link_url": "https://mostlyright.md/d/…",
  "dataset_slug": "kalshi-hourly-btc",
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0"
}
```

`status` is `dataset_access_changed` when the sharing moved, `dataset_access_unchanged` when it was already that way, and `dataset_access_reported` under `--show`. A mode with no address and no public name carries no `public_url`, no `link_url` and no `dataset_slug`.

```json title="mr-data dataset note DATASET_ID --heading '…' --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-narrative.v1",
  "status": "dataset_note_written",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "workspace_id": "39ffafde-86ed-5d7b-b337-1c205ea79390",
  "cell": {
    "cell_id": "chose-kalshi-over-polymarket",
    "heading": "Chose Kalshi over Polymarket for hourly strikes",
    "checkpoint_seq": null,
    "revision_of": null,
    "sequence": 4,
    "cursor": "4-…",
    "appended_at": "2026-09-12T09:00:00Z",
    "payload_digest": "9f3c…",
    "superseded": null,
    "markdown_bytes": 812,
    "block_count": 2,
    "phase": null
  },
  "sealed": false,
  "not_evidence": "…",
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0"
}
```

`--list` answers `status: "dataset_notes_listed"` with `cell_count`, `rendered_count`, `source_truncated` and `cells` numbered `note 1`, `note 2`. A cell is not sealed. No receipt quotes it, and it is not evidence about a run. Every narrative payload asserts `"sealed": false` and refuses a Studio answer that does not.

```json title="mr-data dataset activity DATASET_ID --phase building --message '…' --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-activity.v1",
  "status": "dataset_activity_reported",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "phase": "building",
  "message": "Fetching 20 city stations",
  "transition": "changed",
  "expires_at": "2026-09-12T09:02:00Z",
  "sequence": 12,
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0",
  "facts": { "sources_probed": 18 }
}
```

A report that named no counters carries no `facts`. `transition` says whether the page moved or the expiry alone was renewed.

```json title="mr-data dataset watch DATASET_ID --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-watch.v1",
  "status": "dataset_stream_ended",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0",
  "ended_because": "stream_closed",
  "event_count": 41,
  "last_event": {},
  "stream": { "last_event_id": "41-…", "last_sequence": 41, "events_seen": 41, "reconnects": 0 }
}
```

`ended_because` is `stream_unavailable` when the stream could not be followed at all. `stream` is then `{}`.

```json title="mr-data dataset state DATASET_ID --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-current-state.v1",
  "status": "dataset_state_reported",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "current_state": {},
  "source_inventory": null,
  "recording_membership": {},
  "event_history": {}
}
```

```json title="mr-data dataset archive DATASET_ID --confirm-name '…' --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-archived.v1",
  "status": "dataset_archived",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "name": "Kalshi hourly BTC",
  "version": 9,
  "dashboard_url": "https://mostlyright.md/datasets/d31017e0",
  "note": "The dataset is retired and the title 'Kalshi hourly BTC' is free for another one. Nothing built was deleted: every run, artifact, version and decision cell stays where it is."
}
```

```json title="mr-data dataset categories --json (shape)"
{
  "schema_version": "mostlyright-dataset-categories.v1",
  "version": 1,
  "assignment_policy": "Exactly one primary category, chosen from the dataset purpose, tables and measured entities. …",
  "categories": [{ "id": "crypto", "label": "Crypto", "definition": "Cryptocurrencies, blockchain networks, DeFi and digital-asset markets." }]
}
```

### Errors

| Code | When |
| --- | --- |
| `THIN_REQUEST_INVALID` | A verb that needs `DATASET_ID` got none. `draft` without `--expected-version` or without `--description-file`. A `state` paging flag without its pair. A flag belonging to another writing verb. `archive` without `--confirm-name`. |
| `THIN_CONFIRMATION_MISMATCH` | `archive`, where `--confirm-name` does not match the title Studio holds. Archiving releases the title, so the title confirms it. |
| `THIN_DATASET_VERSION_CONFLICT` | The dataset moved twice under a `set`, or once under an `archive`. Somebody else is editing it. Read it with `mr-data dataset show` and write again. `archive` never retries on its own, because a racing `set` may have rewritten the title that confirmed it. |
| `THIN_CLOUD_PUBLIC_SHARING_DISABLED` | `publish`, where this organization does not allow datasets to be shared publicly. |
| `THIN_CLOUD_PUBLISHER_PROFILE_REQUIRED` | `publish`, where this organization has no publisher name yet. Set it under the team's settings. |
| `THIN_CLOUD_STEP_UP_REQUIRED` | `publish`, where publishing needs a verification only a signed-in browser can finish. Open the dashboard page and share it from there, or hand yourself the page with `mr-data open`. |
| `THIN_NARRATIVE_CELL_TOO_LARGE` | `note`, where the heading runs over 200 characters or the markdown body over 65536 bytes. |
| `THIN_NARRATIVE_CELL_CONTROL_CHARACTER` | `note`, where the body carries a control character other than tab and newline. The command refuses a carriage return rather than stripping it. |
| `THIN_NARRATIVE_REVISION_INVALID` | `note --revise` naming the cell's own identifier. Reusing the identifier is the revision. |
| `THIN_RESPONSE_INVALID` | Studio answered without a `dataset_id`, without a `version` a conditional write needs, without saying how the dataset is shared, or described a narrative cell as sealed. |
| `THIN_ACTIVITY_BUDGET_SPENT` | The automatic activity report ran out of its 1.5-second budget. It never fails the command it rides beside. |

`publish` also carries Cloud's own sentence after this client's, with its author named.

### Example

```bash title="create the page, fill it in, publish it"
DATASET_ID=$(mr-data dataset create --name "US city temperature history" --json | jq -r .dataset_id)
mr-data open "/datasets/$DATASET_ID" --open

mr-data dataset set "$DATASET_ID" \
  --name "US city temperature history, 20 metros" \
  --category climate-environment \
  --topics "weather,temperature,cities" \
  --license CC-BY-4.0 \
  --description-file description.md

mr-data dataset publish "$DATASET_ID" --mode public --json
```

## The category vocabulary

Exactly one primary category, chosen from the dataset purpose, tables and measured entities. Prefer the principal subject over its source, cadence, format or secondary variables. Preserve the assignment on refresh. `other` requires a recorded reason.

| `--category` | Label | Definition |
| --- | --- | --- |
| `climate-environment` | Climate & Environment | Weather, climate, pollution, ecosystems and environmental measurements. |
| `crypto` | Crypto | Cryptocurrencies, blockchain networks, DeFi and digital-asset markets. |
| `finance` | Finance | Banking, securities, investment, insurance and financial-market activity. |
| `economics-business` | Economics & Business | Economic indicators, businesses, commerce, industry and consumer activity. |
| `government-politics` | Government & Politics | Elections, public policy, government institutions and administration. |
| `people-society` | People & Society | Population, demographics, communities, social conditions and human behavior. |
| `health-medicine` | Health & Medicine | Clinical medicine, healthcare, diseases and public health. |
| `science-research` | Science & Research | Physical and natural sciences, experiments, astronomy and general research metadata. |
| `technology` | Technology | Software, computing, AI, internet and telecommunications. |
| `energy` | Energy | Electricity, fuels, energy production, consumption and supply infrastructure. |
| `agriculture-food` | Agriculture & Food | Farming, livestock, fisheries, food production and food systems. |
| `transport-mobility` | Transport & Mobility | Movement of people and goods by road, rail, air and sea. |
| `places-infrastructure` | Places & Infrastructure | Geography, maps, property, buildings, housing and non-energy infrastructure. |
| `education` | Education | Schools, universities, learning, students and educational outcomes. |
| `sports` | Sports | Sports competitions, athletes, teams and athletic performance. |
| `arts-media-entertainment` | Arts, Media & Entertainment | Culture, arts, news media, music, film, gaming, esports and audience activity. |
| `law-public-safety` | Law & Public Safety | Legal systems, courts, crime, policing and emergency response. |
| `other` | Other | A meaningful subject not covered by any category after reviewing its definitions. |

`mr-data dataset categories --json` prints this table from the copy in the wheel, which reads offline.

## cover

```bash
mr-data cover DATASET_ID SUBJECT [--json]
```

`mr-data cover` generates one branded cover image and attaches it to a dataset. Describe what the image should depict. Studio chooses the single-colour style, the palette, the model and the dimensions. Each invocation picks a new curated colour and composition, so run it again for a different cover. There is no style flag.

| Flag | Argument | Required | Meaning | Default |
| --- | --- | --- | --- | --- |
| `DATASET_ID` | id | yes | The dataset the cover goes on. | none |
| `SUBJECT` | text | yes | What the image should depict, in plain language. Style and dimensions are fixed. | none |
| `--json` | none | no | One JSON object instead of human lines. | off |

```json title="mr-data cover DATASET_ID '…' --json"
{
  "schema_version": "mostlyright-thin-client-v4-dataset-cover.v1",
  "status": "dataset_cover_generated",
  "lane": "hosted",
  "dataset_id": "d31017e0",
  "cover_image_url": "https://…",
  "width": 1200,
  "height": 630,
  "media_type": "image/webp",
  "color": { "name": "cobalt", "hex": "#2447C4" },
  "model": "…",
  "style_policy": "mostlyright-single-ink-editorial-v1",
  "estimated_cost_usd": "0.101",
  "sha256": "9f3c…",
  "generated_at": "2026-09-12T09:00:00Z"
}
```

`width`, `height`, `media_type`, `style_policy` and `estimated_cost_usd` are fixed. The command
checks the response against those values before printing its receipt. Any mismatch produces
`THIN_RESPONSE_INVALID`. `color.hex` is `#` followed by six uppercase hexadecimal digits. A
lowercase value indicates drift.

Errors: `THIN_REQUEST_INVALID` for a `SUBJECT` over 600 characters. `THIN_RESPONSE_INVALID` for a drifted answer, a non-HTTPS image URL, a bad `sha256`, or a timestamp that is not `YYYY-MM-DDTHH:MM:SS(.fff)Z`. `THIN_NOT_FOUND` for a dataset that is not yours or does not exist.

```bash
mr-data cover "$DATASET_ID" "a thermometer over a city skyline at dusk" --json
```

## See also

- [CLI reference](/docs/reference/cli/): conventions, exit codes, environment variables
- [Tables](/docs/reference/cli/table/): the tables this dataset holds
- [Runs](/docs/reference/cli/run/): the run-scoped decision record `note` has a twin of
