# emit(parse(x)) === x, byte for byte, or the job says why not

> transactions.dev · by visibility.cloud — 2026-07-31
> Written for the supply-chain ISV / traceability-vendor engineering org — the integration engineer whose one question is "does it round-trip without loss, and can I prove it per job".

One thesis: **a parser you cannot invert is a parser you cannot audit, so the
first honesty gate in this pipeline is byte-exact round-tripping — every
`parse` ships with an emit-and-diff fidelity report, and the two legal
normalizations are recorded as "canonical-equal" with their diff positions,
never silently passed.**

## Why byte-exact, not semantically-equal

You own a decade of interchange traffic. When a tool tells you it parsed your
850s "successfully," the claim you actually need is stronger: that nothing was
dropped, reordered, defaulted, or normalized on the way through — because the
moment you re-emit a document for an acknowledgment, an archive, or a
counterparty dispute, every silent "cleanup" becomes a discrepancy you cannot
explain.

Semantic equivalence is the escape hatch every lossy parser hides behind.
"The data is all there" is unfalsifiable; `emit(parse(x)) === x` is a byte
comparison. So that is the law this layer runs: for every document, `parse`
produces the [lossless canonical JSON](/blog/canonical-json-the-lossless-pivot/) —
segment order preserved, delimiters recorded, every element and component
positioned — and `emit` reproduces the wire bytes from it. The comparison is
part of the job, not a test that ran once in CI.

## The two exceptions, defined precisely

Real traffic contains two classes of byte difference that are legal noise
rather than data loss, and the honest move is to define them narrowly and
report them loudly:

1. **Trailing segment terminator.** Some senders end the interchange with a
   final segment terminator (and sometimes a trailing newline), some do not.
   One canonical rule applies on emit; a document that differs from its
   re-emission only at this position is not byte-exact.
2. **Trailing empty elements.** `N1*ST*ACME**` and `N1*ST*ACME` carry the same
   elements; senders disagree about whether to transmit the empty tail. One
   canonical rule applies; a document that differs only in trailing empty
   elements at segment ends is not byte-exact either.

Neither case is silently passed. Both are recorded as **canonical-equal** — a
distinct verdict, with the diff position — because the difference between
"identical" and "identical after two named normalizations" is exactly the kind
of thing an auditor asks about, and exactly the kind of thing lossy tools
launder into "success."

## The report is a contract

Run the engine over a corpus and the fidelity report is the output that
matters:

```
$ npx transactions.dev parse corpus/*.edi --fidelity --json
```

```json
{
  "documents": 412,
  "byteExact": 397,
  "canonicalEqual": [
    { "file": "850-vendorA-0093.edi",
      "rule": "trailing-empty-elements",
      "positions": [{ "segment": "N4", "offset": 1873 }] },
    { "file": "856-hub-114.edi",
      "rule": "trailing-terminator",
      "positions": [{ "offset": 20471 }] }
  ],
  "refused": 0,
  "silentPasses": 0
}
```

Three properties make this a contract rather than a dashboard number. The
verdict classes are closed: `byteExact`, `canonicalEqual` (with the rule and
position for every instance), or `refused` with a typed error — there is no
fourth bucket where differences go to be forgotten. The `silentPasses` field
is structurally zero: it exists in the report so that its absence is a claim,
not an omission. And the report is per **job**, not per release: every parse
you run produces one, which means the evidence is about *your* corpus, on
*your* bench, today — not about a test suite you have to take on faith.

This is a family law, not an invention of this layer. The event layer at
[epcis.dev](https://epcis.dev) runs the same discipline on its 1.1/1.2/2.0
translation engine — a per-job round-trip fidelity report instead of a
compatibility promise — and the reasoning transfers verbatim: the engineer
who owns ten years of vendor XML, or ten years of interchange archives, does
not need a claim; they need a diff.

## What refusal looks like

A document the round-trip cannot hold is refused, never approximated. The
refusal is typed — `{"error":{"code","message","hint"}}` on stderr, exit
code `2` for unusable input — because "we normalized your file into something
we could handle" is precisely the behavior this law exists to forbid. If a
delimiter collision, an 8-bit character in a free-text element, or
[an envelope](/blog/isa-is-106-characters/) that lies about its own counts
makes lossless representation impossible, the job says so and says where —
and in practice most of what the field calls "EDI is hard" turns out to be
envelope trouble the refusal names precisely.

## Run it on your own corpus

The evaluation this post is pitching is the one you run without us in the
room: take a directory of your real traffic, run `parse --fidelity`, read the
report. The count of byte-exact documents is the claim; the canonical-equal
list with its two named rules is the fine print, printed in full.

The pivot format the round-trip runs through — and the schema that makes it
your integration surface rather than our internal detail — is
[the canonical JSON post](/blog/canonical-json-the-lossless-pivot/). What the
events compiled from those documents look like is
[the 856 worked example](/blog/the-856-is-already-an-aggregation-tree/). And
the access flow at [/get-access/](/get-access/) starts with an address and
asks what you would compile — the corpus you just measured is the right
answer.

---
Access: https://transactions.dev/get-access/ · All posts: https://transactions.dev/blog/ · Machine face: https://transactions.dev/llms.txt
