The blog  ·  2026-07-31

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

Written for ED-1 — 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 — 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. N1STACME* and N1ST*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
{
  "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 door at 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 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. What the events compiled from those documents look like is the 856 worked example. And the access flow at /get-access/ starts with an address and asks what you would compile — the corpus you just measured is the right answer.


830 words. The access flow is at /get-access/; the family's event door is epcis.dev.