Skip to main content
receipt factory is how you drive Factory from a terminal: create an objective, watch it work, steer or abort a running job, read back what happened from the receipt chain, and — through receipt factory agent — do all of that from a script that reads one predictable JSON envelope.
This command tree is reachable only from the in-repo CLI. receipt factory and everything under it requires a Receipt checkout, Bun and Postgres. It is absent from the released receipt binary — the one the installer puts on your machine has no factory command at all. Read The receipt CLI for what the released binary does contain, and Factory concepts and configuration for the vocabulary this page uses.

Reaching the subtree

From a checkout, any of these work:
There is one difference between them worth knowing. The CLI discovers .receipt/config.json by walking up from the working directory, so where you stand matters — except that the .receipt/bin/receipt wrapper changes to its own checkout’s root and exports RECEIPT_REPO_ROOT before it starts, and bun run receipt:factory is that same wrapper. So the first and third forms always target the checkout the wrapper lives in, while bun src/cli.ts factory … follows your current directory. --repo-root or RECEIPT_REPO_ROOT pins the location either way.

How flags are parsed

receipt factory uses the developer CLI’s own parser, described on The in-repo developer CLI. Two of its rules shape the flags on this page: a repeated flag becomes an array, which is what makes --check a --check b and repeated --initial-task accumulate, and a numeric flag throws --<key> must be a number on a non-numeric value before clamping. An unrecognised subcommand fails with Unknown factory subcommand '<x>'.

Shared flags

--execution-target conflicts with the two flags it subsumes:
  • --execution-target conflicts with --execution-path
  • --execution-target conflicts with --computer-provider
Capability values are normalized, and never inferred. The values you pass to --required-capability are split on commas, trimmed, lower-cased, de-duplicated and sorted. Nothing is read out of your prompt text: the CLI ignores the prompt, the title and the initial tasks when deciding required capabilities, and returns only what you passed explicitly. If your objective needs a connected system, name it with --required-capability or --aws. Mentioning a service in the prompt does not request it.
--model is not a receipt factory flag. The Factory worker model is set only through the RECEIPT_FACTORY_TASK_MODEL environment variable.

Initialization

receipt factory init

Writes .receipt/config.json. It resolves the repository root from --repo-root, then the git root of your current directory, then the directory itself, and requires a git repository. --data-dir defaults to .receipt/data relative to that root. --codex-bin takes the flag, then codex if it is on PATH, then RECEIPT_CODEX_BIN, then the literal codex. Interactively, and without --yes, it prints Receipt Factory setup and prompts for the data directory and the Codex executable. Cancelling prints Factory setup canceled. and throws Factory setup canceled. It then shows a spinner labelled Profiling repository: collecting repository status and Factory defaults, followed by Repository profile collected in <dur> and a summary:
Nothing is actually profiled. The “Repository profiling” line is a fixed string — Using checked-in Factory profiles and skills only. — printed whatever your repository contains. The “detected” validation command is always the same hard-coded default, bun run build. Treat the confirmation prompt as your chance to type the real commands, not as a review of something the tool worked out.
It then asks Use detected validation commands?; declining prompts for Validation commands (comma or newline separated). Without --force, an existing file refuses with Factory config already exists at <path>. Output:
  • --json returns {ok, config, profileSummary, environment}, where environment carries bunRuntime, codexPath, codexAvailable, openAiReady, sourceBranch and sourceDirty. openAiReady is hard-coded false and tells you nothing.
  • Interactively and without --yes, it prints Factory config written to <path>, Next: bun run factory and Create objective: bun run factory run --title "Mission" --prompt "Describe the change".
  • Otherwise — non-interactively, or with --yes — it prints factory config written: <path>.
The glyph becomes > when RECEIPT_FORCE_ASCII=1 or TERM=dumb.

Creating objectives and staying attached

receipt factory run

Creates an objective and stays attached to it. The prompt comes from --prompt, then --problem, then the trailing words; with none of them it fails with factory run requires --prompt or trailing prompt text. The title comes from --title, otherwise from the prompt’s first sentence clipped to 96 characters with . How it stays attached depends on the terminal. On an interactive terminal — stdin and stdout both TTYs — and without --json, run opens the board TUI focused on the new objective and leaves when the objective reaches a terminal state. Everywhere else it waits headlessly and reports one progress line:
The bracketed prefix is the last six characters of the objective id, and at most two highlights are shown. When stdout is a terminal the line is rewritten in place; when it is not, the line is printed only when it changes, and at least every 10 seconds. --json suppresses the progress line entirely and prints only the closing snapshot. Either way it polls every 2000 ms and also wakes on runtime events. Exit codes:
Exit code 2 is not a failure. It means Factory stopped and is waiting for you — either the objective is blocked on operator guidance, or it is ready and auto-promotion is off, which is the normal case in a stock checkout.
On the headless wait path — not in the TUI — the CLI enqueues an audit job when the objective reaches status completed, failed, canceled or blocked and no audit job exists yet: payload kind factory.objective.audit, agent factory-control, lane collect, session key factory:audit:<objectiveId>, singleton mode steer, one attempt. It then waits up to 10 seconds for that job, unless you passed --json. If the terminal receipt snapshot is missing it throws cannot audit <objectiveId>: terminal receipt snapshot is unavailable, which is reported as factory audit enqueue failed after terminal objective: <message> and does not change the exit code.

receipt factory create

The same flag surface as run, but it returns as soon as the objective is created. Missing prompt: factory create requires --prompt or trailing prompt text.

receipt factory compose

Without --objective it creates a new objective. With --objective <id> it reacts to the existing objective, attaching the prompt as an operator note. Missing prompt: factory compose requires --prompt or trailing prompt text.
Two things about compose will surprise you.The two output modes disagree on the verb. In the react case the JSON action stays "compose", with the prompt in a note field, while the text renderer prints reacted <objectiveId>. If you are scripting against action, do not expect it to say react.It drops a flag its siblings pass. compose forwards --execution-path but not --computer-provider. run, create and agent start all pass the provider through. If you need the provider set, use --execution-target, which sets both, or use create.

Reading an objective

receipt factory watch and receipt factory inspect

Missing id: factory watch requires <objective-id> / factory inspect requires <objective-id>. watch prints a one-shot snapshot with --json, or whenever the session is not an interactive terminal, and opens the TUI focused on that objective otherwise. inspect never opens the TUI. With --watch it re-renders on an interval — --interval-ms, default 1000, clamped 250 to 60000 — printing only when the rendered output changes, separated by a --- line in text mode. Panels. Exactly nine values are accepted: overview, report, tasks, candidates, evidence, activity, live, debug, receipts
Anything else silently falls back to overview. There is no error and no warning, so a typo in --panel looks like a working command that shows you the wrong thing. The TUI has a tenth panel, analysis, that --panel cannot select — --panel analysis gives you overview.
With --panel omitted, the default is report for an investigation objective that has a synthesized report, or is completed with reports — unless the latest handoff body looks like a Markdown table, in which case it is overview. The JSON payload is {objectiveId, panel, data}, where data is the panel’s own value: the tasks, candidates, evidence cards, activity, live projection, debug projection or recent receipts respectively; overview returns {header, prompt, checks, policy, blockedExplanation, latestDecision} and report returns {objectiveId, objectiveMode, severity, report, synthesized, reports, artifacts}. Text mode prints a header followed by a == <Panel> == section:

Read and analysis commands

Target resolution

parse, investigate, doctor and a targeted audit accept any of an objective id, task id, candidate id, job id, run id, chat id or a raw stream name, and resolve them in this fixed order:
  1. Empty, or the literal latest — the newest objective stream. With none: No objective receipt streams found under <dataDir>
  2. An exact stream name
  3. factory/objectives/<id>
  4. jobs/<id>
  5. Any stream ending /runs/<id>
  6. Any stream ending /sessions/<id> that has no /runs/ in it
  7. Ids starting task_ or containing _candidate_, by scanning objective chains
  8. A substring match on stream names
  9. Otherwise: Unable to resolve factory receipt target '<id>'
The result records which rule matched, in a matchedBy field, so you can see when a fuzzy match found something you did not mean.

receipt factory investigate

The target is optional even though the usage string shows it as required; omitting it resolves the most recently updated objective. --compact changes two defaults: the timeline limit from 20 to 12 (clamped 1–1000) and the context characters from 1200 to 700 (clamped 200–20000). --as-of-ts <epoch-ms> replays the chain as of a timestamp. Text output opens # Factory Receipt Investigation with ## What Happened, ## Latency and ## Assessment. The JSON report carries requestedId, resolved, links, warnings, summary.whatHappened, objectiveMode, window, inputs, outputs, latency, canonicalEvidenceBundle, dag, packetContext, timeline, tasks, candidates, jobs, agentRuns, anomalies, audit, recommendations, autoFixObjectiveId, interventions and assessment. Warnings you may see: Persisted objective audit is stale relative to the latest objective update., Audit recommendation generation failed: <error> (or Audit recommendation generation failed. when no detail was recorded), Invalid canonical evidence bundle at <path>: <error>.

receipt factory audit

--limit defaults to 12, clamped 1–200. Text opens # Factory Receipt Audit with ## Summary, ## Improvement Signals, ## Top Anomalies, ## Auto-Fix Feedback, ## Memory Hygiene, ## Objectives and ## Warnings. A targeted failure reads Failed to audit objective <id>: <message>.

receipt factory insights

--limit defaults to 8, clamped 1–50. The JSON carries schema: "receipt.factory.insights.v1". Text opens # Factory Receipt Insights with ## Summary, ## Improvement Insights, ## Recurring Anomalies, ## Objectives, ## Agent Use and ## Warnings.

receipt factory doctor

receipt factory doctor cannot run in this release. It is listed in the CLI usage, but the review path holds its BYOK key in a variable that no code path sets, and then requires it, so every invocation fails with Organization BYOK key required for factory doctor review. Use investigate and audit instead.

Stream read commands

These four load .receipt/config.json and use its dataDir; --data-dir does not reach them.

JSON switching and output files

--json is not uniform across the subtree. replay, replay-chat, analyze and parse also switch to JSON whenever the session is not an interactive terminal — the check requires both stdin and stdout to be TTYs — so piping them gives you JSON even without the flag. investigate, audit, insights and doctor stay text when piped. inspect formats by --json alone.
--output-file creates parent directories and changes what is printed. In JSON mode it writes the payload and prints {"ok": true, "outputFile": "<abs>", "format": "json", "bytes": <n>, "sha256": "<hex>"}. In text mode it writes the text and prints wrote <abs path>.

Resumption

receipt factory resume

Missing id: factory resume requires <objective-id>. It reacts to the objective and then attaches like run, with the same exit codes. One difference matters if you are scripting: run --json stays silent while it waits, but resume --json keeps printing progress lines before the closing JSON snapshot, so read its output as a stream rather than as one JSON document.
Resume does not revive the old objective. Reacting at a continuation boundary creates a brand-new objective with a fresh id. The default continuation message is Retry the objective from its last durable state. Reuse verified prior evidence, but create fresh task, candidate, and job authority instead of reviving superseded execution ids. The CLI re-reads the new id before it starts waiting, so the id you watch afterwards is not the id you passed.

Objective mutations

  • react takes an optional message; note requires one and appends it without reacting.
  • promote is gated — see the promotion gate for the 409 and the six refusal messages.
  • cancel defaults --reason to the literal string canceled from CLI. It cancels the objective’s jobs, then emits the cancel and handoff receipts.
  • cleanup cancels objective-scoped jobs with the reason factory objective cleanup and removes task worktrees and runtime workspaces.
  • archive cancels jobs with the reason factory objective archived, emits an archive receipt if one is not already there, and rebalances slots.
Every one of these runs inside an optimistic-concurrency retry — four attempts with 25, 50 and 75 ms backoff — that recognises the Expected prev hash … and … advanced before applying a mutation conflicts. Each one except note also asserts the action against the profile’s allowed dispatch actions first; a compose that continues an existing objective asserts react. Text mode prints one line per mutation, <action> <objectiveId>, with two exceptions: note prints noted <objectiveId>, and a compose that carried a note prints reacted <objectiveId>. The JSON form is:
note appears only when a note or prompt was attached. The objective actions are create, compose, note, react, promote, cancel, cleanup and archive.

Job controls

The runtime answers with:
  • 404 job not found
  • 400 steer message required / follow-up message required
  • 409 job <jobId> is <status>; send the follow-up to its objective to continue in a new run
  • 409 job <jobId> became <status>; send the follow-up to its objective to continue in a new run
abort is exempt from the active-status check, so you can abort a job that has already finished. Commands are attributed to factory.cli. Job mutations print <action> queued for <jobId> in text mode; the JSON is {ok, kind: "job", action, jobId, job, commandId}, with the job actions abort, steer and follow_up.

The agent-first surface

receipt factory agent … is the machine-readable half of the subtree. Every action returns one envelope, on both the success and the failure path:
command is factory.agent.<action>, except in the computer lane where it is factory.agent.computer.<subaction> or factory.agent.opensandbox.<subaction>. mode is write for start, create, steer, follow-up, react, resume and for computer exec, run, write, upload and download; it is read everywhere else. On the error path ok is false, data is null, and error is filled in:
The code takes one of two forms: FACTORY_SERVICE_<httpStatus> when the underlying error carried a status, and FACTORY_AGENT_COMMAND_FAILED otherwise. details is present only in the first case. The process exit code is 1 on the error path, so you can branch on the exit status and still read the envelope. --output-file on an agent command writes the full envelope to disk and prints a second, small envelope whose data is {outputFile, format: "json", bytes, sha256}, whose single artifactRef is labelled Factory agent envelope, and whose nextCommands is ["cat <path>"]. receipt factory agent, agent help, agent --help and agent -h print the help envelope before the config is loaded, so they work in a repository with no .receipt/config.json. Every other agent action loads the config first. An unrecognised action returns Unknown factory agent action '<x>' inside an error envelope.

Agent actions

Read-command envelopes suggest what to run next. For an objective those are:

The computer lane

agent opensandbox is an alias. The subaction defaults to status. The target comes from --target, --objective or --objective-id, then the positional argument, then the board’s selected objective; with none: 404 No selected objective; pass an objective id. Every subaction that needs a lease — everything except status and inspect — reports its source as either existing-lease or acquired-lease, which is the distinction that matters:
Leases are not created for you. If the objective already has a recorded lease, the CLI reconnects to it. If it does not, and you did not pass --acquire, the command fails with a 409 and this exact message:No reconnectable OpenSandbox lease is recorded for this objective; pass --acquire to create a disposable probe leaseWith --acquire the CLI takes a disposable lease, scoped by --scope <id> (defaulting to agent-cli:<timestamp>), and records human-readable wait summaries in acquireWaits. A lease created by --acquire is destroyed when the command exits, unless you also pass --keep.
An exec result is {exitCode, signal?, stdout, stderr}, where exitCode can be null.

The interactive board

Bare receipt in an interactive terminal — no arguments — opens the Factory board, as do receipt factory and receipt factory board. Bare receipt passes no flags through, so off an interactive terminal it prints the CLI usage rather than a board. receipt factory and receipt factory board do respond to the flags: with --json, or when the session is not an interactive terminal, they print a snapshot instead of opening the UI: JSON as {compose, board, selected?, live?}, or text under the section headers == Repo ==, == Needs Attention ==, == Active ==, == Queued ==, == Completed ==, == Archived ==, == Selected Objective == and == Live Tasks ==.
The board’s compose panel is not derived from your repository. Its profile summary is always Using checked-in Factory profiles and skills only., its validation commands are always ["bun run build"], and its policy is always the built-in default.

Hotkeys

The panels, in order, are Overview, Report, Tasks, Candidates, Evidence, Activity, Live, Debug, Receipts and Analysis.

Slash commands

/abort-job also accepts abortjob; /follow-up also accepts followup and follow_up. Plain text with no objective selected creates a new objective; with one selected it reacts to that objective. Unknown commands answer Unknown command '/<name>'. Try /help., and every objective-scoped command refuses with its own line when nothing is selected, such as Select an objective before steering its active job.

Commands this page does not cover

receipt factory simulate and receipt factory experiment are engineering harnesses rather than product surface; the simulator is documented in Testing, simulation, and stack validation. receipt factory helper list reads the checked-in helper manifests, and receipt factory helper run executes one through the profile’s Python runner, so run needs python3 on PATH. Note that the usage text is incomplete. The one-line subcommand list — receipt factory [init|run|create|…] — omits agent, board, doctor, simulate and helper; the longer Factory block printed underneath adds agent, doctor and simulate back, but board and helper appear nowhere. There is also no per-subcommand help for run, create, compose, watch, inspect, resume, the mutations, the job controls, board, helper or init — asking for receipt factory run --help prints the whole default usage instead. Next step: run the simulator and the stack checks.