context_id, each group is folded through the assembler on its own, and every
receipt is written to .trace/receipts/<run>~<operation>.json, where
receipt reads it back.
Usage
Arguments and options
Example
receipt show --run
takes.
Events that carry no context_id — capture attestations, a chain confirmation
added out of band — belong to no single operation, so they are reported
separately rather than attributed or dropped:
One build per run
Several runs mean several builds, never one merged event set. An event carries no run id — the run is its file name — andcontext_id is only guaranteed unique
within a run, since an adapter may number operations per session. Folding two
runs together would merge two unrelated payments that happen to share an id. So
each run is read, assembled and reported on its own, and its receipts are stored
under its run id.
If an error interrupts a multi-run build, the message says how many earlier runs
were already written — a store that was half updated must not look untouched.
How a template is resolved
A--template name is looked up in .trace/templates/ first, then in the
templates shipped with the CLI. So a template written by
haia-trace template new is found with no extra flags, and a
local x402-buyer.yaml deliberately shadows the built-in one.
Anything that is not a bare name — ./ops/refund.yaml, an absolute path — is
read as a path to a template file instead. A name that resolves nowhere names
both places searched:
Filtering the view
--status narrows what is shown, never what is written. Receipts are derived
and reproducible, so the store stays complete for
receipt list and receipt show whichever verdict one build
chose to look at.
The summary line reports everything assembled first, with the filtered subset
after it, so a narrowed screen never understates the run:
JSON output for agents
--json emits the full Receipt objects — the
machine-readable basis an agent reads (completeness, missing) to decide
whether to continue a chain of spending.
- Receipts are nested per run rather than flattened, because
operation_idis only unique within its run: a flat list can hold two different payments under one id. assembledis the count before--statusnarrowedreceipts. An agent gating on “no partial receipts” has to tell a run whose every receipt is full (assembled > 0,receiptsempty) from a run that captured nothing (assembled: 0).template.originisbuiltin,localorfile— which of the two same-named templates won is invisible in the receipt itself.
0 on a partial receipt: a verdict is an answer, not a
failure. Branch on completeness yourself.
Producing a run file
build needs an events/*.ndjson run file under the root. Any NDJSON whose
lines match the Event Contract works, and two adapters write one for
you:
@usehaia/trace-x402— attach it to a live x402 client, resource server or facilitator withtrace(instance, { writer: createRunEventWriter(".trace/events") }).@usehaia/trace-circle— record verified Circle webhook v2 deliveries into the same directory, for theescrow-arcflow.
events/ under whatever root you build with.
Build a capture against the template for the side that recorded it — x402-buyer
(the default) for a client, --template x402-seller for a resource server,
--template x402-facilitator for a facilitator, --template escrow-arc for an
escrow. A run that traced two sides in one process assembles into an honest buyer
receipt or an honest seller receipt, one build each, never a merge of the two.
An empty events directory is an error that names the path. A directory holding
runs from an earlier session is not:
build assembles the newest of those
without complaint, so clear out a root you have stopped recording into.