# Trace an EPC in one call.

A mock recall across a co-manufacturing boundary is famously a multi-day exercise: two vendors, three spreadsheets, one phone tree. This post is the same exercise as a transcript that fits on one screen. The claim is mechanical, not aspirational: when events carry computed identity, aggregation history, and organization-grain parties, tracing an EPC is a single query returning a chain a machine can verify. Everything below runs on the committed golden-corpus fixtures; every hash is reproduced verbatim.

## The setup: four hops, two parties

The corpus sequence `sequence-worked-trace-four-hop.json` follows one serialized unit — `urn:epc:id:sgtin:0614141.107346.2017` — through four events: commissioned with its lot at the plant, packed onto SSCC `urn:epc:id:sscc:0614141.0000005501`, received at a DC, received at a store. Two capturing sites, no shared vendor between them — the join is the standard, not a platform.

Wire the door into a session (one config block, from [the MCP page](/mcp/)):

```json
{ "mcpServers": { "epcis": {
    "command": "npx", "args": ["-y", "epcis.dev", "mcp"] } } }
```

The stdio server re-dispatches every tool it serves through the same core the CLI and HTTP door run. Capture the document, then trace:

```
→ capture  { "document": <sequence-worked-trace-four-hop.json> }
← { "accepted": true, "events": 4 }

→ trace { "epc": "urn:epc:id:sgtin:0614141.107346.2017" }
← structuredContent:
{
  "epc": "urn:epc:id:sgtin:0614141.107346.2017",
  "events": [
    { "bizStep": "commissioning", "readPoint": "urn:epc:id:sgln:0614141.00777.0",
      "hash": "ni:///sha-256;ee0c4ea58564b0060736d2c8c6678098b83bdb85de6381ef41295e47814cf971?ver=CBV2.0" },
    { "bizStep": "packing", "parentID": "urn:epc:id:sscc:0614141.0000005501",
      "hash": "ni:///sha-256;f4f3c1438a7c43d35cfddbbf0e6cbdaef0ae4d302b3ba4efd882a81e879d8789?ver=CBV2.0" },
    { "bizStep": "receiving", "readPoint": "urn:epc:id:sgln:0614141.00888.0", "via": "urn:epc:id:sscc:0614141.0000005501",
      "hash": "ni:///sha-256;70676462ccf3b16ad2f8a18acb40d08c0650115f5fb0029f678c8e7ce2af36ff?ver=CBV2.0" },
    { "bizStep": "receiving", "readPoint": "urn:epc:id:sgln:0614141.00999.0",
      "hash": "ni:///sha-256;c4838b4517f980df9851d1113540f84e4f557199da0332fa6dc2a89e442c67ca?ver=CBV2.0" }
  ]
}
```

Recompute the hashes yourself — same fixture, same algorithm, no trust required:

```
$ npx epcis.dev hash sequence-worked-trace-four-hop.json
hash	0	ni:///sha-256;ee0c4ea58564b0060736d2c8c6678098b83bdb85de6381ef41295e47814cf971?ver=CBV2.0
hash	1	ni:///sha-256;f4f3c1438a7c43d35cfddbbf0e6cbdaef0ae4d302b3ba4efd882a81e879d8789?ver=CBV2.0
hash	2	ni:///sha-256;70676462ccf3b16ad2f8a18acb40d08c0650115f5fb0029f678c8e7ce2af36ff?ver=CBV2.0
hash	3	ni:///sha-256;c4838b4517f980df9851d1113540f84e4f557199da0332fa6dc2a89e442c67ca?ver=CBV2.0
```

## What makes the answer evidence, not a report

Three properties, each doing distinct work:

**Identity is computed, so the chain is checkable.** Each hop carries its CBV §8.9 content hash — identity as a pure function of the event. Any party holding the same events computes the same chain; a tampered hop computes a different hash and falls out. A trace answer without content-derived identity is a story; with it, the answer is a verifiable artifact.

**The containment graph is walked, not stored.** Hop 3 is the subtle one: the DC never scanned the unit. It scanned the *pallet* — and `trace` resolves the unit's presence through the packing event's parent/child assertion (`"via"` names the SSCC). The pallet's journey becomes the case's journey by graph logic over AggregationEvents, not by a `parent_id` column somebody keeps current. Partial depalletization and re-aggregation stay honest for the same reason: history is appended, never overwritten.

**The parties are on the events.** Each hop carries its capturing account and, at organization grain, its source/destination parties — so the co-man boundary is *in* the chain, not a join against someone's vendor master. And the performer grain stays uncollapsed: `spine:who` on these fixtures is the attested observer at each hop (a line lead, a pack-out crew, a dock crew — resolvable identities, [id.org.ai](https://id.org.ai)'s Agent. Human. Thing.), while `spine:capturedBy` is the warrantor account stamped at the door. Who saw it and who stands behind the record are different questions; a dispute needs both answered separately — [the performer model in full](/epcis-who-performer-worker-agent/).

## Timed, for the skeptic upstairs

The corpus trace — capture four events, walk the graph, return the chain — completes in milliseconds on a laptop: the whole transcript above is a coffee-break demo, which is the point your deputizing engineer forwards. The contrast is not against a slow computer; it is against the multi-day phone tree that answers the same question when records live in five ungoverned shapes. The recall framing of this same machinery is in [the FSMA series](/fsma-204-cte-kde-epcis-mapping/); the executive rendering of a trace — custody evidence, not JSON — is [visibility.cloud](https://visibility.cloud).

For an agent integrating this: the tools are typed, reads are pure, refusals are typed results (`isError: true` mirroring the door's problem document, never prose to regex), and paginated tools lift continuations into `structuredContent.nextPageToken`. The store is append-only at every door — corrections arrive as new events carrying `errorDeclaration`, so a chain once returned can be re-derived later and the audit view still contains its own mistakes, visibly. Exit codes on the CLI face are stable and load-bearing: `0` ok, `1` negative domain verdict, `2` usage — a failed trace is a verdict to branch on, not a crash to catch. The full contract ships in the tarball as `AGENTS.md` — read it where you already are: `node_modules/epcis.dev/AGENTS.md`.

**Run the walkthrough now:** `npx epcis.dev mcp` — the corpus, the tools, and the hashes above, on your bench, no account. **Trace over a durable record** — events your whole estate captured last quarter, not this session: the hosted query surface rides a capture key — **[get one here](/get-a-key/)**.

---

**Proof, not adjectives.** This build could not verify the gateway's test suite green, so no sentence on this page claims a passing run — the last verified run is recorded in the repository's own CI, not here. Live on this origin, no key: POST /translate, /validate, /hash. No conformance attestation has ever been issued. The dated ledger is /what-ships-today/.
