---
title: Drive relate from an agent
description: "relate, the embedded Cypher graph database (relatedb.ai): Run relate from an agent through the CLI, with JSON output, an error object and exit codes to branch on, a row cap, parameters, and a time bound per statement."
type: how-to
surfaces:
  - cli
  - agents
status: stable
verified: 0.1.2
verified_by:
  - cypher
  - cli
canonical_url: https://relatedb.ai/docs/guides/agents/drive-relate-from-an-agent/
prerequisites:
  - get-started/install
related:
  - reference/cli/agent-mode
  - reference/cli/exit-codes-and-errors
  - reference/errors
  - guides/agents/agent-safety
  - guides/cli/scripting
  - reference/cli/flags
last_updated: 2026-11-09
---

# Drive relate from an agent

Run relate from an agent through the CLI, with JSON output, an error object and exit codes to branch on, a row cap, parameters, and a time bound per statement.

> Verified against relate 0.1.2. If your `relate --version` is newer, read https://relatedb.ai/docs/reference/changelog/#since-0.1.2

This guide shows you how to drive relate from an agent through the CLI: read the briefing, ask for the [envelope](https://relatedb.ai/docs/reference/glossary/#envelope), pass values as parameters, branch on the error object and the [exit code](https://relatedb.ai/docs/reference/glossary/#exit-code), read the [row cap](https://relatedb.ai/docs/reference/glossary/#row-cap), pick an output mode, and put a time bound on each statement. Each `relate` command is one process that runs one statement or a `;`-separated list and exits, so an agent calls it like any other shell tool.

## Prerequisites

- relate on `PATH` ([Install relate](https://relatedb.ai/docs/get-started/install/)).
- A shell the agent can run commands in. No server and no configuration file are involved.

## Read the briefing

`relate agent-context` prints the commands, query-run flags, and exit codes of the binary that is installed, so an agent works from the version it has rather than one it remembers. `relate agent-context <command>` prints one command's synopsis, flags, and examples.

The briefing ends with the exit codes, the part an agent branches on:

```bash verify
relate agent-context | tail -7
```

```text
Exit codes:
  0  success: success, including an empty result
  1  fatal_error: runtime failure: store open or I/O error, capacity limit, query execution failure, internal error
  2  usage_error: unknown flag, bad or missing value, extra positional
  5  conflict: write-write conflict, or the store is locked by another opener
  6  validation_error: the statement is rejected: syntax, semantic or type error
  7  timeout: a statement exceeded --timeout
```

Before them it lists the twelve commands and every query-run flag; the [agent quickstart](https://relatedb.ai/docs/get-started/agent-quickstart/#read-the-briefing) shows the whole output.

## Create the example graph

The steps use one small graph: three people and the two projects they work on. This writes a new single-file store, `team.rl`, from a seed script:

```bash verify
cat > team.cypher <<'EOF'
CREATE (ada:Person {name: 'Ada', role: 'engineer'}),
       (grace:Person {name: 'Grace', role: 'engineer'}),
       (noor:Person {name: 'Noor', role: 'maintainer'}),
       (relate:Project {name: 'relate', language: 'Rust'}),
       (tck:Project {name: 'opencypher-tck', language: 'Gherkin'}),
       (ada)-[:WORKS_ON {since: 2024}]->(relate),
       (grace)-[:WORKS_ON {since: 2023}]->(relate),
       (grace)-[:WORKS_ON {since: 2022}]->(tck),
       (noor)-[:MAINTAINS]->(relate),
       (ada)-[:KNOWS]->(grace);
EOF
```

```bash verify
relate query --database ./team.rl --mode envelope < team.cypher
```

```text
{"columnTypes":[],"columns":[],"notifications":[],"planCache":"miss","rows":[],"timings":{"elapsed_ms":3.2087079999999997,"execute_ms":2.911125,"lex_parse_ms":0.01425,"lower_ms":0.021417,"parse_ms":0.279333,"plan_ms":0.012667,"result_generation_ms":0.000125,"rewrite_ms":0.0005},"truncated":false}
```

Every later command on this page opens `team.rl` with `--read-only`, which opens the store [read-only](https://relatedb.ai/docs/reference/glossary/#read-only): relate never writes it, and a write fails with exit code 6.

## Ask for the envelope

`--mode envelope` prints one compact JSON object per result set, on one line:

```bash verify
relate --database ./team.rl --read-only --mode envelope 'MATCH (p:Person)-[w:WORKS_ON]->(j:Project) RETURN p.name AS person, j.name AS project, w.since AS since ORDER BY since'
```

```text
{"columnTypes":["STRING","STRING","INTEGER"],"columns":["person","project","since"],"notifications":[],"planCache":"miss","rows":[{"person":"Grace","project":"opencypher-tck","since":2022},{"person":"Grace","project":"relate","since":2023},{"person":"Ada","project":"relate","since":2024}],"timings":{"elapsed_ms":0.454708,"execute_ms":0.2725,"lex_parse_ms":0.034041999999999996,"lower_ms":0.042833,"parse_ms":0.113917,"plan_ms":0.059333000000000004,"result_generation_ms":0.00025,"rewrite_ms":0.008042},"truncated":false}
```

| Key             | Holds                                                                                                                   |
| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `columns`       | The column names in query order; present even when there are no rows                                                    |
| `columnTypes`   | One type name per column, taken from the first row; `ANY` when that value is null or there are no rows                  |
| `rows`          | One object per row, with keys sorted alphabetically; `columns` gives the query's order                                  |
| `truncated`     | `true` when the row cap cut the rows, otherwise `false`                                                                 |
| `rowCap`        | Present only when the row cap cut the rows: `{"kept": N, "total": M}`                                                   |
| `notifications` | The query notifications; always present, `[]` when there are none                                                       |
| `plan`          | Present only on an `EXPLAIN` or `PROFILE` result                                                                        |
| `planCache`     | Whether a cached plan served the statement, such as `miss` or `hit-template`                                            |
| `timings`       | The statement's phase timings in milliseconds, such as `elapsed_ms` and `execute_ms`; the values change from run to run |

The [output modes reference](https://relatedb.ai/docs/reference/cli/output-modes/#timings-and-the-plan-cache) defines every timing key and `planCache` value.

An empty result keeps its columns, so "no rows" is distinguishable from "no output":

```bash verify
relate --database ./team.rl --read-only --mode envelope "MATCH (p:Person {name: 'Zed'}) RETURN p.role AS role"
```

```text
{"columnTypes":["ANY"],"columns":["role"],"notifications":[],"planCache":"miss","rows":[],"timings":{"elapsed_ms":0.42174999999999996,"execute_ms":0.222083,"lex_parse_ms":0.031291,"lower_ms":0.047541,"parse_ms":0.127041,"plan_ms":0.063667,"result_generation_ms":0.00033299999999999996,"rewrite_ms":0.009458},"truncated":false}
```

A run of several statements prints one envelope per statement, one per line, so the output is JSON Lines. New keys may be added to the envelope; existing ones keep their names.

## Pass values as parameters

`--param N=V` binds the parameter `$N`, and repeats for each parameter. `V` is parsed as JSON, so it can be a number, `true`, a quoted string, an array, or a map; a bare word is a string. Single quotes around the statement keep the shell from expanding `$names` before relate sees it:

```bash verify
relate --database ./team.rl --read-only --mode envelope --param 'names=["Ada","Noor"]' --param since=2023 'MATCH (p:Person)-[w:WORKS_ON]->(j:Project) WHERE p.name IN $names AND w.since >= $since RETURN p.name AS person, j.name AS project'
```

```text
{"columnTypes":["STRING","STRING"],"columns":["person","project"],"notifications":[],"planCache":"miss","rows":[{"person":"Ada","project":"relate"}],"timings":{"elapsed_ms":0.440333,"execute_ms":0.248584,"lex_parse_ms":0.033417,"lower_ms":0.049166,"parse_ms":0.12237500000000001,"plan_ms":0.060208,"result_generation_ms":0.000292,"rewrite_ms":0.008542000000000001},"truncated":false}
```

`--fetch-params N=SRC` reads a JSON file, or fetches an `http` or `https` URL, and binds the whole document as `$N`. In this example the document is a map, read from a file the first command writes:

```bash verify
printf '%s\n' '{"names": ["Grace"], "since": 2022}' > filter.json
relate --database ./team.rl --read-only --mode envelope --fetch-params filter=./filter.json 'MATCH (p:Person)-[w:WORKS_ON]->(j:Project) WHERE p.name IN $filter.names AND w.since >= $filter.since RETURN p.name AS person, j.name AS project ORDER BY project'
```

```text
{"columnTypes":["STRING","STRING"],"columns":["person","project"],"notifications":[],"planCache":"miss","rows":[{"person":"Grace","project":"opencypher-tck"},{"person":"Grace","project":"relate"}],"timings":{"elapsed_ms":0.475125,"execute_ms":0.269167,"lex_parse_ms":0.036791,"lower_ms":0.050665999999999996,"parse_ms":0.131708,"plan_ms":0.064667,"result_generation_ms":0.000334,"rewrite_ms":0.008792},"truncated":false}
```

A fetch is capped at 64 MiB and 60 seconds and has no URL allowlist. [Keep an agent safe](https://relatedb.ai/docs/guides/agents/agent-safety/#control-network-egress) lists it with the other flags that reach the network.

## Branch on the exit code and the error object

A failed statement prints its message on stderr and exits with the code of its class. In `--mode envelope`, `--json`, and `--jsonl` it also prints an error object on stdout. This statement uses `$name` without binding it:

```bash verify
relate --database ./team.rl --read-only --mode envelope 'MATCH (p:Person {name: $name}) RETURN p.role AS role'
echo "exit=$?"
```

The first line is stderr, the second is stdout:

```text
semantic error: ParameterMissing: Expected parameter(s): name
{"error":{"code":"validation_error","exit_code":6,"kind":"semantic","message":"semantic error: ParameterMissing: Expected parameter(s): name","retryable":false}}
exit=6
```

The object's `code` names the class and always comes with the same exit code. `retryable` is `true` for a write-write conflict, which a fresh transaction can fix, and for a `--concurrent-logical-workspace-limit` refusal while other statements hold the workspace pool:

| Exit | `code`             | Remedy                                                                                                                                                                                                                                                                              |
| ---- | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0    | `success`          | Read the rows; an empty result is still a success                                                                                                                                                                                                                                   |
| 1    | `fatal_error`      | Read `message`: a missing store, an I/O error, a capacity limit, a `LOAD CSV` path outside its directory. The same command fails again, except a workspace pool refusal with `retryable: true` (kind `resource`), which can run again once other statements finish                  |
| 2    | `usage_error`      | Fix the command line: an unknown flag or a bad value. No error object is printed                                                                                                                                                                                                    |
| 5    | `conflict`         | With `retryable: true` (kind `conflict`), run the statement again in a fresh transaction. With `retryable: false` (kind `locked`), another process holds the store: wait for it to exit. A directory store can also be opened `--read-only` beside its writer; a `.rl` store cannot |
| 6    | `validation_error` | Fix the statement: syntax, a missing parameter, a type error, a safe mode refusal, or a write on a read-only store                                                                                                                                                                  |
| 7    | `timeout`          | Bound the work with `LIMIT` or a narrower pattern, or raise `--timeout`                                                                                                                                                                                                             |

A syntax error adds `column` and `source`, which point at the failing token. A run of several statements that stops partway adds `statement`, `statements`, and `committed`, so the agent knows whether earlier statements already changed the store:

```bash verify
relate --mode envelope 'RETURN 1 AS a; RETRN 2; RETURN 3 AS c'
echo "exit=$?"
```

```text
{"columnTypes":["INTEGER"],"columns":["a"],"notifications":[],"planCache":"miss","rows":[{"a":1}],"timings":{"elapsed_ms":0.315959,"execute_ms":0.17925,"lex_parse_ms":0.019666,"lower_ms":0.028791,"parse_ms":0.088292,"plan_ms":0.039541,"result_generation_ms":0.00020800000000000001,"rewrite_ms":0.006834},"truncated":false}
syntax error: parse error: unexpected token "RETRN"
  RETRN 2
  ^
statement 2 of 3 failed; 1 completed and committed; 1 not run
{"error":{"code":"validation_error","column":1,"committed":1,"exit_code":6,"kind":"syntax","message":"syntax error: parse error: unexpected token \"RETRN\"   RETRN 2   ^","retryable":false,"source":"RETRN 2","statement":2,"statements":3}}
exit=6
```

The first statement printed its envelope and committed, the second failed, and the third never ran. The [exit codes and errors reference](https://relatedb.ai/docs/reference/cli/exit-codes-and-errors/) lists every field.

## Read the row cap

Under a detected agent harness, with stdout not a terminal, relate prints at most 100 rows per statement. A cut result says so three ways: a warning on stderr, `"truncated": true` with a `rowCap` object in the envelope, and a `{"rowCap": …}` line after the rows in `--mode jsonl`, just before the statement's timing line. The exit code stays 0. In this command `CLAUDECODE=1` stands in for a harness, and `grep` keeps the `rowCap` line:

```bash verify
CLAUDECODE=1 relate --mode jsonl 'UNWIND range(1, 250) AS x RETURN x' 2>/dev/null | grep '"rowCap"'
```

```text
{"rowCap":{"kept":100,"total":250}}
```

`--max-rows N` sets the cap everywhere, a terminal included, and `--max-rows 0` lifts it. With a cap of 2, the warning comes first on stderr, then the envelope:

```bash verify
CLAUDECODE=1 relate --mode envelope --max-rows 2 'UNWIND range(1, 250) AS x RETURN x'
```

```text
warning: result truncated to 2 rows (the statement returned 250); pass --max-rows 0 for all rows
{"columnTypes":["INTEGER"],"columns":["x"],"notifications":[],"planCache":"miss","rowCap":{"kept":2,"total":250},"rows":[{"x":1},{"x":2}],"timings":{"elapsed_ms":0.494834,"execute_ms":0.344042,"lex_parse_ms":0.023459,"lower_ms":0.038375,"parse_ms":0.095,"plan_ms":0.047416,"result_generation_ms":0.001209,"rewrite_ms":0.007292},"truncated":true}
```

The cap limits what is printed, not what the engine computes: the engine builds the whole result first. A `LIMIT` or an aggregate in the statement bounds the work itself.

## Pick an output mode

Without `--mode`, the output depends on where stdout goes: the human table on a terminal, JSON Lines in a pipe, and TOON in a pipe under [agent mode](https://relatedb.ai/docs/reference/glossary/#agent-mode). TOON is a compact table: one `rows[N]{columns}:` header line, then one line per row, with nested values as inline JSON, closed by the statement's `timings:` and `planCache:` entries.

```bash verify
CLAUDECODE=1 relate --database ./team.rl --read-only 'MATCH (p:Person) RETURN p.name AS name, p.role AS role ORDER BY name'
```

```text
rows[3]{name,role}:
  Ada,engineer
  Grace,engineer
  Noor,maintainer
timings:
  {"elapsed_ms":0.43470899999999996,"execute_ms":0.252333,"lex_parse_ms":0.032792,"lower_ms":0.044083,"parse_ms":0.11637499999999999,"plan_ms":0.056542,"result_generation_ms":0.00020800000000000001,"rewrite_ms":0.008208}
planCache:
  miss
```

To compare the modes on one result, run a statement in the terminal on this page. It runs in your browser on an empty database opened read-only, as `relate --read-only` opens one, and prints the bytes relate prints in `table`, `envelope`, `jsonl` and `toon`, with the exit code; the timings it prints are the engine's in the page, so their numbers differ from a run of the binary. The `row` column holds a map, which TOON and JSON Lines print as inline JSON and the table prints in Cypher's notation:

```cypher run name=pairs
UNWIND [{name: 'Ada', since: 2024}, {name: 'Grace', since: 2023}] AS p RETURN p.name AS person, p.since AS since, p AS row
```

```table
| person  | since | row                          |
|---------|-------|------------------------------|
| "Ada"   | 2024  | {name: "Ada", since: 2024}   |
| "Grace" | 2023  | {name: "Grace", since: 2023} |
2 rows
```

The statement as relate 0.1.2 prints it in four of its output modes, rendered from this page's recorded result by the terminal's renderers, which match the relate 0.1.2 binary byte for byte on the recorded runs, timing values aside. The recording keeps no timings, so each one shows as `<ms>`; relate prints the milliseconds of its own run, and the live terminal prints the engine's. Exit code 0, as relate 0.1.2 returns it.

`--mode table` prints six lines on stdout:

```bash
relate --read-only --mode table "UNWIND [{name: 'Ada', since: 2024}, {name: 'Grace', since: 2023}] AS p RETURN p.name AS person, p.since AS since, p AS row"
```

```text
+---------+-------+------------------------------+
| person  | since | row                          |
+---------+-------+------------------------------+
| "Ada"   | 2024  | {name: "Ada", since: 2024}   |
| "Grace" | 2023  | {name: "Grace", since: 2023} |
+---------+-------+------------------------------+
```

`--mode envelope` prints one line on stdout:

```bash
relate --read-only --mode envelope "UNWIND [{name: 'Ada', since: 2024}, {name: 'Grace', since: 2023}] AS p RETURN p.name AS person, p.since AS since, p AS row"
```

```text
{"columnTypes":["STRING","INTEGER","MAP"],"columns":["person","since","row"],"notifications":[],"planCache":"miss","rows":[{"person":"Ada","row":{"name":"Ada","since":2024},"since":2024},{"person":"Grace","row":{"name":"Grace","since":2023},"since":2023}],"timings":{"elapsed_ms":<ms>,"execute_ms":<ms>,"lex_parse_ms":<ms>,"lower_ms":<ms>,"parse_ms":<ms>,"plan_ms":<ms>,"result_generation_ms":<ms>,"rewrite_ms":<ms>},"truncated":false}
```

`--mode jsonl` prints three lines on stdout:

```bash
relate --read-only --mode jsonl "UNWIND [{name: 'Ada', since: 2024}, {name: 'Grace', since: 2023}] AS p RETURN p.name AS person, p.since AS since, p AS row"
```

```text
{"person":"Ada","row":{"name":"Ada","since":2024},"since":2024}
{"person":"Grace","row":{"name":"Grace","since":2023},"since":2023}
{"planCache":"miss","timings":{"elapsed_ms":<ms>,"execute_ms":<ms>,"lex_parse_ms":<ms>,"lower_ms":<ms>,"parse_ms":<ms>,"plan_ms":<ms>,"result_generation_ms":<ms>,"rewrite_ms":<ms>}}
```

`--mode toon` prints seven lines on stdout:

```bash
relate --read-only --mode toon "UNWIND [{name: 'Ada', since: 2024}, {name: 'Grace', since: 2023}] AS p RETURN p.name AS person, p.since AS since, p AS row"
```

```text
rows[2]{person,since,row}:
  Ada,2024,{"name":"Ada","since":2024}
  Grace,2023,{"name":"Grace","since":2023}
timings:
  {"elapsed_ms":<ms>,"execute_ms":<ms>,"lex_parse_ms":<ms>,"lower_ms":<ms>,"parse_ms":<ms>,"plan_ms":<ms>,"result_generation_ms":<ms>,"rewrite_ms":<ms>}
planCache:
  miss
```

Under a detected agent harness, relate prints at most 100 rows per statement; this statement returns two rows, so the cap leaves it whole.

Live: the statement runs in your browser on an empty read-only database, as `relate --read-only` opens one, and the pane shows what relate prints in the mode you pick, with the exit code and, under "Run as an agent harness", the row cap; the statement can be edited or swapped for a preset.

Turn on "Run as an agent harness", pick the 250-row preset and run it to see the cap cut the rows and the warning on stderr; the exit code stays 0.

An explicit `--mode` always wins, so a pipeline that parses JSON pins it:

| Mode       | Prints                                                                          | A row-cap cut shows as                          |
| ---------- | ------------------------------------------------------------------------------- | ----------------------------------------------- |
| `envelope` | One JSON object per result set, with column types                               | `"truncated": true` and `rowCap`                |
| `jsonl`    | One JSON object per row, then one timing line per statement                     | A `{"rowCap": …}` line before the timing line   |
| `json`     | One JSON object per result set, the rows in `data`; one compact line when piped | A `rowCap` key beside `data`                    |
| `toon`     | A header line, one line per row, then `timings:` and `planCache:` entries       | A `rowCap:` entry before `timings:`             |
| `markdown` | A GitHub-flavored Markdown table                                                | A closing `> truncated: N of M rows shown` line |
| `csv`      | CSV; a value starting with `=`, `+`, `-` or `@` gets a leading `'`              | The stderr warning only                         |
| `table`    | The human table                                                                 | The stderr warning only                         |

Agent mode turns on the 100-row cap and the TOON default. It follows one order: `--agent-mode` or `--no-agent-mode`, then `RELATE_AGENT_MODE=1` or `0`, then harness detection. The flag and the variable print a one-line notice on stderr; detection is silent. The [agent mode reference](https://relatedb.ai/docs/reference/cli/agent-mode/#detection) lists the variables that count as a harness.

## Bound each statement

`--timeout` cancels a statement that runs longer than the duration given (`500ms`, `10s`, `2m`; a bare number is seconds) and exits 7. Without it, a statement has no time bound:

```bash verify
relate --mode envelope --timeout 100ms 'UNWIND range(1, 20000) AS a UNWIND range(1, 20000) AS b RETURN count(*) AS pairs'
echo "exit=$?"
```

```text
Error: query timed out after 100ms
{"error":{"code":"timeout","exit_code":7,"kind":"execution","message":"query timed out after 100ms","retryable":false}}
exit=7
```

`--read-only` keeps a reading agent from writing. [Keep an agent safe](https://relatedb.ai/docs/guides/agents/agent-safety/) adds safe mode, backups before a write, and the permission matrix.

## Verify

One read-only statement shows the whole contract: machine output on stdout, and an exit code the agent can test.

```bash verify
relate --database ./team.rl --read-only --mode envelope 'MATCH (p:Person) RETURN count(p) AS people'
echo "exit=$?"
```

```text
{"columnTypes":["INTEGER"],"columns":["people"],"notifications":[],"planCache":"miss","rows":[{"people":3}],"timings":{"elapsed_ms":0.348958,"execute_ms":0.177792,"lex_parse_ms":0.031208999999999997,"lower_ms":0.044917,"parse_ms":0.109583,"plan_ms":0.053583,"result_generation_ms":0.000292,"rewrite_ms":0.008167},"truncated":false}
exit=0
```

## If it goes wrong

- **`semantic error: ParameterMissing: Expected parameter(s): <name>`** (exit code 6): the statement uses `$<name>` and no `--param` or `--fetch-params` binds it. Pass `--param <name>=<value>` ([errors reference](https://relatedb.ai/docs/reference/errors/#engine-parameter-missing)).
- **A syntax error whose `source` has a gap where `$<name>` was** (exit code 6): the statement was in double quotes, so the shell replaced `$<name>` with an empty string before relate saw it. With the parameter inside a map, the message starts `syntax error: parse error: unexpected token "}" (RightBrace)`. Single-quote the statement.
- **`warning: result truncated to 100 rows (the statement returned <n>); pass --max-rows 0 for all rows`** (exit code 0): the row cap cut the output. Pass `--max-rows 0`, or bound the statement with `LIMIT` or an aggregate ([errors reference](https://relatedb.ai/docs/reference/errors/#cli-row-cap-warning)).
- **`Failed to open Relate: storage locked: <path>`** on stderr, with the error object message `storage locked: <path>` (exit code 5, `retryable: false`): another process has the store open. Wait for it to exit; a directory store can also be opened `--read-only` beside its writer ([errors reference](https://relatedb.ai/docs/reference/errors/#cli-conflict-locked)).

## Related

- [Agent mode](https://relatedb.ai/docs/reference/cli/agent-mode/): the row cap, the TOON default, and the harness variables.
- [Exit codes and errors](https://relatedb.ai/docs/reference/cli/exit-codes-and-errors/): every exit code and every field of the error object.
- [Errors](https://relatedb.ai/docs/reference/errors/): every message with its cause and its remedy.
- [CLI flags](https://relatedb.ai/docs/reference/cli/flags/): `--param`, `--fetch-params`, `--max-rows`, `--timeout`, and the rest.
- [Script the CLI](https://relatedb.ai/docs/guides/cli/scripting/): multi-statement scripts, transactions, and stdin.

## Terms used on this page

- **envelope**: One compact JSON object per result set, printed by `--mode envelope`, that keeps the column list even when there are no rows.
- **exit code**: The number a `relate` run returns to the shell; each nonzero value names a class of failure, such as 2 for usage, 5 for conflict, 6 for a rejected statement.
- **row cap**: The limit on rows printed per statement, 100 by default under a detected agent harness; `--max-rows` sets it, and a cut is reported in-band.
- **read-only**: An open mode (`--read-only`) that never writes, checkpoints or migrates the store; writes fail, and a directory store can be read beside its writer.
- **agent mode**: CLI defaults for a detected agent harness on a non-terminal stdout, or forced on: at most 100 rows printed per statement, the cut reported, TOON in a pipe.
