Skip to main content
From a checkout you can read any run’s receipt chain — 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. 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:
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

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:
JSON mode returns { "stream": "…", "receipts": [ { "index", "ts", "isoTs", "type" } ] }.

receipt replay

Dumps the full body of every receipt as { "stream": "…", "receipts": [ … ] }.
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.

receipt inspect

Always JSON, and returns { "stream": "…", "count": <n>, "head": <receipt body|null> }.
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.

receipt fork

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

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.
--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:
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

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

--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

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": { … } }.
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.

receipt jobs wait

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

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

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

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:
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

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

receipt memory prefs

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

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

Without a Postgres connection string the failure is:
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 avoids the second one: it changes to the repository root and exports RECEIPT_REPO_ROOT before it starts.
Next step: see how Factory turns an objective into tasks.