Skip to content

receipts

Proof the other model actually read it.

Anyone can claim their tool had a second agent review the code. A receipt is the artifact that settles it: one HTML file holding the whole run, written on your machine, that you can hand to a reviewer who was not there.

What it is

Add --receipt to any run and ccx writes one self-contained HTML file when the run finishes. No stylesheet, no script, no image, no font, nothing fetched. Open it on a plane, six months later, on a machine that never had ccx installed, and it renders.

ccx --receipt "fix the off-by-one in sumAll"
   receipt → ~/.claudecodex/receipts/<run id>.html

It is written before anything else happens at the end of a run, so a failure anywhere downstream cannot lose it. It is free and it stays free.

What it proves

That a model from a different company read this specific change and either found something or did not. Everything in the file is a thing the run actually produced, so the claim can be checked rather than taken:

  • Which agent wrote and which reviewed, and how the pair decided that — including what the free local router predicted and whether the paid conference agreed with it.
  • The argument itself, as a transcript, timestamped from the start of the run. The conference and the review are one conversation, so it reads in order.
  • Every finding with its failing input. A finding only reaches the file if the reviewer could name inputs or state producing a wrong result, a crash, or data loss. That is why each one has a concrete case next to it rather than an adjective.
  • Findings the author refused, with its stated reason. A run that ended in a standing disagreement says so, and shows both sides.
  • The diff the reviewer read. Not your repository — the change, which is the only thing it was shown.
  • The guard that ended the run, in the same plain words the terminal used, plus the number of agent calls spent against the cap of twenty.
  • Every round, not just the last. Up to twelve rounds of history, so a fix that made things worse is visible rather than averaged away.

A clean receipt is not a certificate. It says a rival model read this diff and reported nothing that cleared the burden of proof. It does not say the code is correct, that the task got done, or that a file nobody touched still works. The limits are on the docs page and they are short.

What is in the file

The shape below is the whole file. A receipt is built by reading these fields off the run, and there is nothing in it that is not one of them.

FieldWhat it holds
runId22 characters drawn from 128 random bits. It is the filename and, if a run is ever shared, the URL.
createdAtWhen the run finished, as an ISO timestamp.
ccxVersionThe version that produced it.
taskWhat you typed, to a ceiling of 2000 characters.
domainOne of seventeen fixed categories the router classifies work into. Anything unrecognised becomes general.
conferenceHow the driver was chosen: rounds, elapsed time, winner, the stated reason, what the router predicted, and whether it agreed.
driverWhich agent wrote, its exit code, and its error if it had one.
reviewerWhich agent read the diff. Always the other one.
loopThe stop state and its sentence, the round count, the best round's finding count, whether it regressed, whether the change collapsed, the changed-line count before and after, and the files touched.
loop.historyUp to twelve rounds, each with the findings that round produced.
findingsWhat was still open at the end. Each is a summary, a failing scenario, a file, and a suggested fix.
refusedFindings the author declined, each with the reason it gave. Nothing else from the dispute record travels.
diffThe change the reviewer read, capped at 120KB — the reviewer never saw more than that either.
transcriptUp to 400 lines of the conversation, each tagged claude, codex or ccx and offset in milliseconds from the start.
callsAgent calls spent, against the cap.

What never goes in it

The list above is not filtered down from something larger. The builder reads those named fields and nothing else, and there is no place in it where the run's state is copied wholesale. A field that was never read cannot leak, which is a different and stronger guarantee than a denylist somebody has to remember to extend.

Absent by construction

  • The path to your repository
  • Your working directory
  • The git remote and the branch name
  • Your hostname and your username
  • Anything from the environment
  • The dispute record's internals

Two builds, one source

  • The local file is yours, unredacted, written to ~/.claudecodex/receipts/ with permissions for you alone.
  • The outbound copy is a different build of the same run with the redactor in front of it. --receipt=- streams that one, because a file you are about to paste into a pull request is leaving the machine.

The redactor, before anything leaves

The bytes in a receipt are a source diff, which means they are also .env values, bearer tokens, private keys, connection strings, and whatever customer data was sitting in a fixture. Any share feature that leaks a key once is dead forever and deserves to be. So the outbound build runs a fixed ladder of rules, in this order, and the order matters: a known key shape carries its own name into the report, and an entropy match does not.

  • Secret files are dropped whole, never partially scrubbed. The .env family in both spellings, .pem, .key, .p12, .pfx, .jks, keystores, SSH private keys, .npmrc, .netrc, .tfvars, .htpasswd, service-account JSON, and any file whose name says credentials and whose extension says data. The interesting part of a change to .env is that it happened, not what the value was.
  • Private key blocks are removed entire, from BEGIN to END.
  • Sixteen known key shapes are matched and named — Anthropic, OpenAI, GitHub tokens and PATs, Slack, AWS access and session ids, Google API keys and OAuth tokens, Stripe, SendGrid, npm, GitLab, DigitalOcean, JWTs, and the credentials inside a connection string. Only the match is replaced, so the surrounding code stays readable and the report tells you which credential to go and rotate.
  • A line assigning to a secret-ish name is replaced whole, because AUTH_HEADER: Bearer abc123 hides the credential behind an ordinary-looking first word.
  • Your identity comes out: repository path, home path, username, email address.
  • An entropy sweep catches in-house token formats nothing above knows the shape of. The threshold is 4.05 bits per character, which sits just above the 4.0 that pure hex tops out at, so git SHAs and content hashes fall under it by construction rather than by an allowlist somebody maintains.

Redacted or nothing. The builder takes the scrubber as an argument. The local build passes a pass-through and marks the result as unredacted; the outbound build passes the real redactor; and the upload path refuses any object whose redaction record is missing. There is no code path from raw text to a socket, and that holds because of how the code is arranged rather than because somebody remembered to call the redactor.

And a person looks, every time. There is no --share-yes. Sharing needs a terminal and a typed y on every single run, because CI is precisely where the secrets are and a flag that skips the eyeball is the mechanism by which a key eventually ships.

When it refuses

Over-firing is the correct direction to be wrong in, so the rules are deliberately eager and a second gate catches the result. More than 25 replacements, or more than a fifth of the diff's lines altered, and it will not publish at all. A diff that is mostly redaction marks is not a review anyone can read, and shipping it would be a worse outcome than saying no.

You can watch the whole thing run without sending anything. This prints the exact bytes an upload would carry, after redaction, with a count per rule:

ccx --share-dry "fix the off-by-one in sumAll"
   would upload 41,208 bytes — nothing was sent

The byte count is whatever your run produces. A redactor you cannot audit is worth nothing, and one you have to pay to inspect is worse, so this is free and every rule is a literal in SECRET_FILE_RULES and TOKEN_RULES in src/share.mjs.

The hosted page at /r

A receipt is a file, and a file is awkward to put in a pull request. The intended answer is a link: a run at share.claudeandcodex.com/r/<id>, rendered server-side, no JavaScript, strict content policy, listed nowhere, and noindex, nofollow. The id is 22 unguessable characters and the link is the only way in. Whoever holds the delete token can destroy it without an account, and deleting removes the stored object rather than hiding the link.

Nothing has been uploaded, because nothing can be yet. The server is written and the client's upload function exists, but no flag in the CLI reaches it: bin/claudecodex.mjs does not import upload, and --share-dry is deliberately the only thing on that path a user can run. Today a receipt is a local file and that is the whole of it.

Which is also why there is no example run on this page. The one thing on this site that must never be faked is a receipt, since the entire claim is that a rival model genuinely reviewed the code. A mocked-up run with invented findings would be exactly the lie the artifact exists to make impossible. When there is a real one, it goes here, and it will be a link to a run you can check.

Illustration · not a real run · no receipt has been published

run<22-character id>
taskthe task you typed
wroteclaude or codex, and why the pair chose it
reviewedthe other one
stoppedone of the eleven stop states, in plain words
findingseach with the input that would have broken
diffthe change the reviewer was shown
callsspent, against a cap of 20

The layout of a real receipt, with every value replaced by a description of it. This is a diagram of the fields listed above, not a run.

Whether the hosted half is ever switched on is decided by a public test rather than by wanting to: the terms are on the pricing page. In the meantime, what someone who was sent a receipt sees is a page of its own.

Written against src/share.mjs, bin/claudecodex.mjs and worker/, and correct as of 31 July 2026. If a sentence here and the code disagree, the code is right and this is a bug: angus@angusangus.com.