einvoice / Parity board

Parity board: does the checker still agree with the official rules?

Last run: . Every figure below comes from that run of parity_board.py, which grades the same invoices with our pure-Python engine and with the official Schematron (run through Saxon) and counts where the two verdicts differ. No figure on this page is typed by hand; no run, no update.

This run

LegOfficial ruleset (vendored)AgreementComparisonsDivergences
EN 16931 coreCEN EN16931-UBL Schematron 1.3.16 (last update 2026-03-30)100%250,201 of 250,201 agree0
XRechnung BR-DE (German CIUS)KoSIT XRechnung Schematron 2.5.0 (for XRechnung 3.0.2, compiled 2026-02-04)100%81,092 of 81,092 agree0

Corpus: 1,200 distinct invoice documents across both legs. Engine: einvoice 0.2.10, 297 business rules in total (the rule_count that einvoice info reports).

What “agreement” means here

One comparison is one (invoice, rule) pair. It agrees when both sides fire the rule on that invoice, or both stay silent. So the denominator is graded invoices × rules compared, and a single wrong verdict on one invoice costs exactly one comparison.

Most pairs agree because both sides stay silent, and a rule that never fired at all would score well on silence alone. The row that guards against that is both fired: how many of the agreeing pairs are real violations that the official Schematron and the engine both reported. The corpus contains targeted mutations (each breaks exactly the field one rule guards) so that rules are exercised in the failing direction as well.

EN 16931 core

Official rulesetCEN EN16931-UBL Schematron 1.3.16 (last update 2026-03-30)
Invoices assembled1,153
Invoices graded1,153
Skipped (a side could not process the file; excluded from the counts)0
Rules compared217
Agreement250,201 / 250,201 = 100%
Both fired (true-positive agreements)17,273
Rules the official side fired at least once217 of 217
Held out of this legBR-DEC-13, BR-DEC-15 (at most 2 decimals on the invoice total VAT amount). In the vendored UBL Schematron these two asserts carry a predicate that can never match, so they never fire; the engine checks the intended rule anyway, which would count as a false positive on every violating invoice. Both rules are compared on the CII leg, where the official test works.

Divergences by rule id

None in this run: on every graded invoice, for every compared rule, the engine and the official Schematron gave the same verdict.

XRechnung BR-DE (German CIUS)

Official rulesetKoSIT XRechnung Schematron 2.5.0 (for XRechnung 3.0.2, compiled 2026-02-04)
Invoices assembled1,069
Invoices graded1,067
Skipped (a side could not process the file; excluded from the counts)2 — cen-unit/BR-CL-23#t0: the official Schematron raised an error; cen-unit/BR-CL-23#t1: the official Schematron raised an error
Rules compared76 (55 XRechnung CIUS rules: BR-DE, BR-DEX, BR-TMP, CVD; plus 21 Peppol rules that KoSIT bundles into the same Schematron)
Agreement81,092 / 81,092 = 100%
Both fired (true-positive agreements)7,087
Rules the official side fired at least once76 of 76

Divergences by rule id

None in this run: on every graded invoice, for every compared rule, the engine and the official Schematron gave the same verdict.

Exactly which official rules

The official artifacts are vendored (copied into the repository and pinned), never fetched at run time. The versions below are read out of those vendored files by the board script:

Upstream check on 2026-09-23 (the public GitHub releases list): a newer KoSIT XRechnung Schematron release exists upstream: v2.6.0, published 2026-09-02. This run graded against the vendored 2.5.0, not the newer release, so these figures say nothing yet about agreement with v2.6.0; no CEN EN16931 Schematron release newer than the vendored 1.3.16 was listed.

What this does not show

Reproduce it

From a source checkout, in einvoice/, with saxonche installed: python3 differential.py en and python3 differential.py xrechnung print the same per-rule tables this board is built from; python3 parity_board.py re-runs both and rewrites this page. Both sides' verdicts are memoised on disk: the official one keyed on the Schematron file's hash and the invoice bytes, ours on the invoice bytes and a fingerprint of every engine source file, so any change to the engine forces a fully live re-grade.