Dalteno← Back to the proof section

Engineering case study · October 2026

docrecon: a reconciliation engine built to be judged

A small, open-source component we built so a prospective partner can evaluate our engineering without an NDA, a sales call or a reference check. Everything below is measured on the published code; nothing is a client figure.

What it is. A library that reconciles the three documents behind a sea shipment — commercial invoice, packing list, bill of lading — against 21 rules, and explains every finding.

Why this problem. It is the shape of work we have done for a customs-document comparison system for an Egyptian broker (figures and material not disclosed). We chose a problem we understand rather than a generic to-do app.

Where. github.com/dalteno/docrecon (MIT) · live demo on dalteno.com.

The brief we gave ourselves

Measured

MetricValueHow measured
Rules21, each with a stable id, severity and one-line contractRULES in web/docrecon.js; RuleCatalogue in C#; both counted by a test
Shared cases52 (15 adversarial)cases/generate.mjs; expectations hand-written as RULE|key sets
JS tests62 passingnode --test web/tests/cases.test.mjs
.NET tests136 defined (52 expectation + 52 golden-parity + 32 unit)dotnet test, xUnit; parity compares every field of every finding to the JS golden report — see the CI badge in the repo for the current run
Mutation checks3 of 3 caughtDisabling R05, flipping the rounding mode, and changing the tolerance comparison each turned the suite red
Throughput38 µs per bundle1,000 bundles in 38 ms, Node 22, after warm-up, mixed cases
Engine size276 lines JS · 389 lines C# corewc -l on docrecon.js, Reconciler.cs + Numeric.cs
Floating-point operations0All arithmetic in integer minor units (BigInt / BigInteger) — ADR-0002
Runtime dependencies0Neither implementation imports anything outside the platform
Effortone engineering day, AI-assistedDesign, both implementations, cases, CI, this page. We use AI tooling the same way we would on your project: it drafts, the tests decide.

Three decisions worth reading

1. No floats, no division

Money is parsed to cents, quantities and weights to thousandths, counts to integers. 3 × 0.10 is 0.30, not 0.30000000000000004. Tolerances are basis points, tested as |a − b| × 10000 > bp × max(|a|, |b|) — no division, so the C# and JS results cannot drift by a rounding step. Case C15_float_trap exists to keep it that way.

2. The cases are the specification

One consistent synthetic bundle plus a named mutation per case, with the expected finding set written by hand. The generator never calls the engine. Separately, the JS engine's full reports are frozen as golden files the .NET engine must reproduce field by field. A rule change therefore shows up three times in review: the generator diff, the golden diff, and both engines.

3. Fail loud, never guess

A quantity of "ten" is a finding (R21), not an exception and not a silent zero. A missing document is reported once and every rule that needs it steps aside. Nothing is inferred from free text; a consignee name is compared only after a documented normalisation (case, punctuation, one trailing legal suffix).

What went wrong, and what it taught us

The first mutation check exposed a gap: flipping the rounding mode from half-away-from-zero to truncation did not fail any shared case, because the ±0.01 amount tolerance absorbed the one-cent difference. Only a unit test caught it. We added C17b_half_cent_zero_tolerance — the same half-cent input with tolerance set to zero — so the behaviour is pinned at the integration level too. The lesson generalises: a tolerance that makes a rule forgiving also makes its tests forgiving; test the boundary with the tolerance off.

Day two: docflow — the workflow around the reconciliation

The same discipline applied to the process the documents live in: a dossier of seven customs documents routed to six roles, with SLAs, a reconciliation gate that reuses docrecon unchanged, an ERP outbox with idempotency keys, notifications on every hand-off, and closure only when every required document is approved and integrated. Built as an event-sourced pure function — every command carries its date, there is no clock — so a visitor can drive it in any order and replay is byte-identical. Try it at dalteno.com/lab/docflow/.

MetricValueHow measured
Rules16 (F01–F16)RULES in web/docflow.js; FlowRules in C#; counted by a test
Document types / roles7 / 6DEFAULT_DEFINITION — data, not code; the roles are the real roles of the process
Shared cases41 (9 adversarial)cases-docflow/generate.mjs; expectations hand-written as status + document states + rule hits
JS tests45 passingnode --test web/tests/docflow.test.mjs — includes the F13 invariant (one notification per hand-off) on every case
.NET tests87 defined (41 expectation + 41 golden-parity + 5 unit)dotnet test; parity compares the whole report, key-order-insensitive
Mutation checks5 of 5 caughtDisabled F08, flipped SLA boundary, removed integration cancel on rejection, froze reconciliation re-run, disabled F02 — each turned the suite red
Throughput73 µs per dossier replay1,000 replays in 73 ms, ~20 events each, Node 22
Engine size247 lines JSwc -l web/docflow.js
Red lineno writes to operational ERP tablesIntegration is an outbox to DOCFLOW_STG.STG_<TYPE> staging tables; the ERP team's job/procedure moves data in. ADR-0004.

Two things the second day taught us. First, a rule that is tested through a tolerance is only half tested — the same lesson as day one, now on dates: the SLA boundary (exactly N days = no breach) needed its own case. Second, hand-writing expectations for a stateful engine forces you to think about every date in a scenario: two of our first drafts accidentally crossed an SLA boundary and would have "expected" a breach we did not intend. The generator caught the author, not the engine — which is the point.

What this does not prove

If this were your project

We would start exactly here: one page of scope with acceptance criteria, a fixed-price pilot of two to four weeks, the shared-cases approach from day one so you can read our tests before our code, and the right to stop after week one. Write to hello@dalteno.com.

© 2026 Dalteno, Cairo · All sample data in docrecon is synthetic. Client names and figures are withheld under NDA.