> ## Documentation Index
> Fetch the complete documentation index at: https://docs.kentron.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Reading receipts and driving jobs

> Resolve a run to a stream, read and fork its receipt chain, audit every chain for integrity, and operate the job queue, memory and session search from a checkout.

From a checkout you can read any run's [receipt chain](/core/receipts-and-streams) — the ordered, hash-linked record of everything that happened in it — end to end, branch it at a chosen point, audit every chain in the database for integrity, and drive the work queue directly — enqueue a job, wait for it, steer it, follow up on it, abort it. The same commands reach agent memory and full-text search over past chat sessions.

All of them belong to the [developer CLI](/cli/from-source/overview). None of the commands on this page exists in the released binary.

## How a target is resolved

Every command that takes `<run-id|stream>` resolves it the same way:

1. If the value contains a `/`, it is used as a stream name **verbatim**. No lookup happens.
2. Otherwise the store's stream list is searched for an exact name match.
3. Failing that, it is searched for any stream ending in `/runs/<value>`.

If none of those match:

```
Unable to resolve run/stream '<value>'
```

So `receipt trace agents/triage/runs/run_abc` addresses a stream directly, while `receipt trace run_abc` searches for it.

## Reading a chain

### `receipt trace`

```bash theme={null}
receipt trace <run-id|stream> [--json] [--output-file <path>]
```

Prints one line per receipt. Text mode is the index right-aligned to four characters, the ISO timestamp, and the event type, which falls back to `unknown` when a receipt has none:

```
   0  2026-01-01T00:00:00.000Z  task.requested
   1  2026-01-01T00:00:03.120Z  task.completed
```

JSON mode returns `{ "stream": "…", "receipts": [ { "index", "ts", "isoTs", "type" } ] }`.

### `receipt replay`

```bash theme={null}
receipt replay <run-id|stream> [--output-file <path>]
```

Dumps the **full body** of every receipt as `{ "stream": "…", "receipts": [ … ] }`.

<Note>
  `receipt replay` is always JSON. `--json` is accepted by the parser and then ignored — there is no text mode, whether or not you pass the flag.
</Note>

### `receipt inspect`

```bash theme={null}
receipt inspect <run-id|stream> [--output-file <path>]
```

Always JSON, and returns `{ "stream": "…", "count": <n>, "head": <receipt body|null> }`.

<Warning>
  `head` is the **last** entry in the chain — the newest receipt, not the first one. If you are looking for what started a run, read index `0` with `receipt trace` or `receipt replay` instead.
</Warning>

### `receipt fork`

```bash theme={null}
receipt fork <run-id|stream> --at <index> [--name <branch-stream>]
```

Writes branch metadata pointing at a position in an existing chain.

* `--at` is required (`--at is required`) and must be a finite, non-negative number (`--at must be a non-negative number`). It is floored.
* `--at` past the end of the chain is rejected by the runtime with `Cannot fork <stream> at <n>; valid range is 0..<length>`.
* `--name` defaults to `<stream>/branches/fork_<base36 timestamp>_<index>`.

It prints `{ "ok": true, "stream": "…", "at": <n>, "branch": "…" }`.

## Auditing every chain: `receipt dst`

```bash theme={null}
receipt dst [<prefix>] [--prefix <prefix>] [--context] [--json] [--limit <n>] [--strict] [--output-file <path>]
```

`receipt simulate` is an undocumented alias for the same command.

It walks every stream in the receipt store — optionally narrowed to those whose name starts with a prefix, given positionally or with `--prefix` — and audits each one for three things: chain **integrity**, **replay**, and **determinism**. `--context` additionally audits the Factory task context packets (manifest, context pack, prompt, memory script).

The report counts streams by kind. The kinds it recognises are:

`factory.objective`, `job`, `agent.history`, `agent.control`, `eval.run`, `computer_use.session`, `generic`

Streams are sorted failures first, then by descending receipt count, then by name, so the interesting ones are at the top of both output modes.

```json theme={null}
{
  "scannedAt": "…",
  "dataDir": "…",
  "streamCount": 12,
  "kinds": { "factory.objective": 0, "job": 0, "agent.history": 0, "agent.control": 0, "eval.run": 0, "computer_use.session": 0, "generic": 0 },
  "statusCounts": {},
  "integrityFailures": 0,
  "replayFailures": 0,
  "deterministicFailures": 0,
  "streams": []
}
```

`--limit <n>` affects **text output only**; the text renderer shows 20 streams by default and appends `- ... <n> more stream(s) omitted` when it truncates. The JSON report always contains every stream.

### `--strict` is the gate

Without `--strict`, `receipt dst` reports failures and exits `0`. With `--strict`, it prints the report first and *then* throws when any of the integrity, replay or deterministic failure counts is non-zero — including the equivalent counts from the `--context` audit when you asked for one:

```
error: DST audit found receipt issues
```

That message goes to standard error and the process exits `1`. `--strict` is the flag to put in a check script.

## Driving jobs: `receipt jobs`

```bash theme={null}
receipt jobs <list|enqueue|wait|steer|follow-up|abort> …
```

`list` is also the default when you give no subcommand. Anything unrecognised throws `Unknown jobs subcommand '<x>'`.

Every subcommand reads and writes the job queue in Postgres, so all of them work with no Resonate server running. Only `enqueue` also asks Resonate to dispatch the new job, and that request is fire-and-forget: with no broker reachable the job is still recorded as `queued`, the command still succeeds, and nothing starts the work. Every `jobs` and `abort` call opens a Resonate poll connection regardless, so with the broker down each one also prints a `Resonate poll disconnected; retrying` warning to standard error.

### `receipt jobs list`

```bash theme={null}
receipt jobs [list] [--status queued|leased|running|completed|failed|canceled] [--limit <n>] [--output-file <path>]
```

`--limit` defaults to 50 and is clamped to 1…500; a non-numeric value falls back to 50. `--status` is passed straight through without validation. Output is always JSON: `{ "jobs": [ … ] }`.

### `receipt jobs enqueue`

```bash theme={null}
receipt jobs enqueue <agent-id> [--lane chat|collect|steer|follow_up] [--payload-json '<json>'] \
  [--job-id <id>] [--max-attempts <n>] [--session-key <key>] [--singleton-mode allow|cancel|steer]
```

A missing agent id throws `agent id is required`. `--payload-json` defaults to `{}` and must parse as a JSON object: valid JSON that is not an object throws `--payload-json must be a JSON object`, and text that is not JSON at all surfaces the parser's own `JSON Parse error: …`. `--max-attempts` defaults to `2` and the backend clamps it to 1…8; a non-numeric value throws `--max-attempts must be a number` before anything is enqueued. Output is `{ "ok": true, "job": { … } }`.

<Warning>
  An invalid `--lane` or `--singleton-mode` value is **silently dropped** rather than rejected. The command then enqueues with the backend defaults — lane `collect` and singleton mode `allow` — so a typo produces a job that runs on the wrong lane with no error. Check the returned job object rather than assuming your flag took effect.
</Warning>

### `receipt jobs wait`

```bash theme={null}
receipt jobs wait <job-id> [--timeout-ms <n>] [--output-file <path>]
```

Polls every 200 ms with a default timeout of 15 000 ms. A missing id throws `job id is required`; an unknown one throws `job not found: <jobId>`. Always JSON: `{ "job": { … } }`.

### `receipt jobs steer` and `receipt jobs follow-up`

```bash theme={null}
receipt jobs steer <job-id> [--payload-json '<json>']
receipt jobs follow-up <job-id> [--payload-json '<json>']
```

Each queues a `steer` or `follow_up` queue command against the job, attributed to `receipt-cli`. An unknown job throws `job not found: <jobId>`. Output is `{ "ok": true, "jobId": "…", "commandId": "…" }`.

### `receipt jobs abort` and `receipt abort`

```bash theme={null}
receipt jobs abort <job-id> [--reason <text>]
receipt abort <job-id> [--reason <text>]
```

Both route to the same handler. `--reason` defaults to `abort requested`, and the command is attributed to `receipt-cli`. A missing id throws `job id is required`.

## Memory: `receipt memory`

```bash theme={null}
receipt memory <read|search|summarize|commit|diff> <scope> …
receipt memory prefs <list|add|remove> …
```

A missing subcommand throws `memory subcommand is required`; an unknown one throws `Unknown memory subcommand '<x>'`.

### Every call needs a scope and an actor

All subcommands except `prefs` take a `<scope>` positional; without it they throw `memory scope is required`.

All of them, `prefs` included, require an actor identity — a user id and an organization id, from flags or from the saved CLI session. The errors name the subcommand:

```
memory.<sub> requires --user-id or RECEIPT_CONNECT_USER_ID
memory.<sub> requires --organization-id or RECEIPT_CONNECT_ORGANIZATION_ID
```

Under `prefs` the prefix is `memory.prefs.<sub>` instead. Because the saved session seeds `RECEIPT_CONNECT_USER_ID` and `RECEIPT_CONNECT_ORGANIZATION_ID`, signing in with `receipt login` is normally enough and you never pass the flags. Every call records an audit entry carrying the user id, organization id, `source: "cli"`, `actor: "cli"` and the command name.

### The subcommands

| Subcommand  | Syntax                                      | Limits and required flags                                                                                                                  |
| ----------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| `read`      | `receipt memory read <scope>`               | `--limit` default 20, clamped 1…500                                                                                                        |
| `search`    | `receipt memory search <scope> [query…]`    | `--query <text>` or trailing text, required — `memory search requires --query or trailing query text`; `--limit` default 20, clamped 1…500 |
| `summarize` | `receipt memory summarize <scope> [query…]` | `--query`; `--limit` default 20, clamped 1…500; `--max-chars` default 2 400, clamped 100…12 000                                            |
| `commit`    | `receipt memory commit <scope> [text…]`     | `--text <text>` or trailing text, required — `memory commit requires --text or trailing text`; `--tags a,b,c` is comma-split and trimmed   |
| `diff`      | `receipt memory diff <scope>`               | `--from-ts <epoch-ms>` **required** — `memory diff requires --from-ts`; `--to-ts <epoch-ms>` optional                                      |

`read`, `search`, `summarize` and `diff` are always JSON and honour `--output-file`. `commit` prints its JSON directly and ignores `--output-file`.

### `receipt memory prefs`

```bash theme={null}
receipt memory prefs [list] [--scope layered|repo|global] [--repo-root <path>] [--output-file <path>]
receipt memory prefs add [text…] [--text <text>] [--scope …]
receipt memory prefs remove <entry-id> [--scope …]
```

The subcommand defaults to `list`; an unknown one throws `Unknown memory prefs subcommand '<x>'`.

`--scope` defaults to `layered` and takes three values:

* `layered` — the repo scope first, then the global scope
* `repo` — the repo scope only
* `global` — the global scope only

The repo scope key is derived from `--repo-root` or the detected git root. `list` returns `{ "scopes": [ … ], "entries": [ … ] }`. `add` requires `--text` or trailing text (`memory prefs add requires --text or trailing text`) and returns `{ "entry": … }`. `remove` requires an entry id (`memory prefs remove requires an entry id`) and rejects an unknown one with `Unknown preference entry '<id>'`, returning `{ "removed": …, "entryId": "…", "scope": "…" }`.

## Session search: `receipt sessions`

```bash theme={null}
receipt sessions search [query…] [--query <text>] [--limit <n>] [--repo-key <k>] [--profile <id>] [--session-stream <stream>] [--output-file <path>]
receipt sessions read <chat-id|session-stream> [--limit <n>] [--output-file <path>]
```

`sessions` queries the projected session messages in Postgres directly and needs **no** actor identity. A missing subcommand throws `sessions subcommand is required`; an unknown one throws `Unknown sessions subcommand '<x>'`.

* `search` runs a Postgres full-text search and requires a query, from `--query` or trailing text (`sessions search requires --query or trailing query text`). `--limit` defaults to 10, clamped 1…100. Output is `{ "results": [ … ] }`.
* `read` requires a target (`sessions read requires a chat id or session stream`). A target containing `/sessions/` is treated as a session stream; anything else is treated as a chat id. `--limit` defaults to 200, clamped 1…1 000. Output is `{ "messages": [ … ] }`.

## Prerequisites, by group

| Commands                                                 | What they need                                                                 |
| -------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `trace`, `replay`, `inspect`, `fork`, `dst` / `simulate` | Postgres, via `ZERO_UPSTREAM_DB`                                               |
| `sessions`                                               | Postgres                                                                       |
| `memory`, `memory prefs`                                 | Postgres, plus an actor identity from flags or a saved session                 |
| `jobs`, `abort`                                          | Postgres. A reachable Resonate server only to dispatch what `enqueue` records. |
| `setup`, `login`, `connect`, `workspace`, `tools`, `mcp` | Network and a reachable gateway. No Postgres.                                  |
| `whoami`, `logout`                                       | Nothing but the local session file. No network, no Postgres.                   |

Without a Postgres connection string the failure is:

```
Receipt Postgres storage requires ZERO_UPSTREAM_DB.
```

<Warning>
  Every developer CLI command loads the runtime module at **import time**, before the handler for your command runs — and before the arguments are even dispatched, so `receipt help` and `receipt whoami` fail exactly like `receipt trace`. Two configuration problems therefore fail the command with an error that never mentions the command you typed:

  * A `.receipt/config.json` that is not a JSON object throws `Factory config must be a JSON object`.
  * With no config file and no git repository, resolving the runtime throws `Factory runtime config requires a git repository or RECEIPT_REPO_ROOT. cwd=<cwd> failed: git -C <cwd> rev-parse --is-inside-work-tree`.

  If a command fails before printing anything, look at your config file and your working directory, not at the stream or job you asked for. The [`.receipt/bin/receipt` wrapper](/cli/from-source/overview) avoids the second one: it changes to the repository root and exports `RECEIPT_REPO_ROOT` before it starts.
</Warning>

Next step: [see how Factory turns an objective into tasks](/cli/from-source/factory-overview).
