The blog · 2026-07-31
Reconcile is a verdict, not a report
Written for ED-2 — the RFID systems-integrator / VAR practice — the practice lead who has to prove value in the receiving bay at go-live.
One thesis: reconciliation between a shipment's paperwork and its physical receipt is only worth deploying when it returns a machine verdict — a typed diff with an exit code — because the moment that decides an integration's fate is a go-live in a receiving bay, not a dashboard review three weeks later.
The go-live problem, stated honestly
You run an integration practice. You have sold the reader infrastructure, commissioned the portals, tuned the antennas, and the customer's acceptance criterion is one sentence: prove the system sees what arrives. On go-live day the evidence you actually possess is two artifacts that have never met:
- the ASN — the 856 or DESADV the supplier sent, claiming what left the dock: SSCCs, GTINs, quantities, lots;
- the event stream — what the portals captured: ObjectEvents and AggregationEvents, timestamped, with EPCs the readers actually saw.
Every practice has improvised this comparison in a spreadsheet at least once, at 6am, with the customer's ops manager watching. The improvised version has two failure modes: it is not repeatable, and it renders an opinion instead of a verdict. "Looks like everything came in" is not an acceptance artifact.
The verdict shape
transactions.dev reconcile takes the document and the events and returns the difference, typed:
$ npx transactions.dev reconcile 856-20260728.edi \
--against events-20260728.jsonl --json
{
"document": "urn:epcglobal:cbv:bt:0614141000012:SHIP20260728002",
"po": "urn:epcglobal:cbv:bt:0724142000017:PO-8813",
"claimed": { "pallets": 4, "cases": 192 },
"observed": { "pallets": 4, "cases": 190 },
"verdicts": [
{ "class": "matched", "gtin": "00614141073467", "lot": "2026A",
"claimed": 144, "observed": 144 },
{ "class": "missing", "gtin": "00614141073467", "lot": "2026B",
"claimed": 48, "observed": 46, "delta": -2 },
{ "class": "unexpected", "gtin": "00614141073474",
"observed": 2, "note": "no ASN line covers this GTIN" }
],
"verdict": "DISCREPANT",
"exit": 1
}
The exit code is the contract: 0 when shipped and seen agree, 1 when they do not, 2 when the inputs are unusable. That is the same CLI law the whole family runs — payload on stdout, typed errors on stderr, verdicts as exit codes — so the reconcile step drops into a go-live script, a nightly cron, or an acceptance test without a human in the middle. Wire it to the gate: the lane goes live when reconcile exits zero three receipts in a row.
Two verdict classes in that transcript deserve a sentence each. missing is the claim-side story: the ASN said 48, the portal saw 46, and now the shortage conversation starts from a machine record instead of a recollection. unexpected is the integrity-side story: something came through the door that no paperwork covers — mis-pick, substitution, or a mislabeled case — and it was caught at the threshold, not at the customer complaint.
The report is a contract, not a printout
Every field name in that transcript is documented surface, not incidental output. The closed verdict-class vocabulary — matched, missing, unexpected, lot_mismatch — the top-level verdict, and the 0/1/2 exit semantics are the report contract this site publishes, worked lot-by-lot against a second fixture in the dock-door post: same shape, different shipment, including the lot_mismatch class a quantity-only comparison can never see. Contract stability is what makes the verdict wirable: your go-live script greps "verdict" and branches on the exit code, and neither moves under you between engagements. And because the comparison is a pure function of two sets, the report re-runs: a portal that flushes buffered reads after an outage changes the observed set, and the same ASN then upgrades this morning's missing to matched without a human edit — the verdict history is itself evidence of when the site knew what.
Why the join is what makes this legal
The comparison only works because both sides speak the same identifiers, and that is core-spec machinery, not product glue. The ASN side is compiled by the join verb: the HL tree becomes the expected aggregation structure, and the document's identity rides along as bizTransactionList entries typed desadv and po — core CBV fields, not extensions. The event side is whatever your capture infrastructure already produces, verbatim; when the discrepancy is settled and a receiving advice goes back upstream, that answer already has a CBV name too — recadv.
Nothing in the reconcile path writes to the event stream. The events stay what the portals observed; the documents stay what the partner claimed. The verb's entire job is the difference — which also means it is safe to run retroactively over last quarter's receipts the day it is installed, and the backlog of quiet shortages it surfaces on day one is, in practice, the strongest sales artifact a practice lead can walk into a renewal with.
What this is not
Not a TMS, not a yard system, not a claims platform, and not a promise that paperwork disputes disappear. A carrier will still argue. What changes is what they are arguing against: a conformant event record with a typed diff against the shipper's own ASN — evidence a counterparty can re-verify without joining anything, which is the family's one sentence and the reason the executive layer at visibility.cloud can stand on records this layer produces. Your customer's auditors read the verdict; their agents can pull the same verdict through the MCP door with the same access a person would use.
At go-live, run reconcile, print the transcript, and hand it over. The acceptance meeting gets very short when the artifact is a verdict.
901 words. The access flow is at /get-access/; the family's event door is epcis.dev.