Zwei Standards, 26 Kantone · Teil 10 von 14
10 · Ein Kapitel für Implementierende — die Drift, und das Loch, das wir kennen
Das vorige Kapitel hat den Standard von aussen vermessen. Dieses öffnet die Dateien.
Dieses Kapitel ist für die Leute, die den Kanal tatsächlich bauen. Es hat zwei Befunde. Der erste: Es sind drei eCH-0276-Schemata gleichzeitig im Umlauf, zwei davon tragen denselben Namensraum, dieselbe Versionsangabe und dieselbe Pflicht-Attributdeklaration — und unterscheiden sich in 88 stillen Datentypwechseln, die kein Änderungsverzeichnis nennt. Der zweite: Die swissdec-Richtlinien verlangen an zwei Stellen ein Testkennzeichen, das in keinem der mitgelieferten Schemata existiert. Ein solches Loch hat genau eine Ausfallrichtung, und wir haben in Deutschland teuer gelernt, wie diese Richtung aussieht.
Drei Dateien, ein Name — und wer sie liefert
Wer heute «eCH-0276» sagt, muss dazusagen, welche Datei er meint. Wir haben alle drei am 04.08.2026 geöffnet und gezählt.
| Datei (SHA-256, gekürzt) | Herkunft | Namensraum | version | Elementnamen |
|---|---|---|---|---|
eCH-0276-1-0.xsd (14a0d6cb…) | ech.ch, Version 1.0.0, Beschluss 02.12.2024, Publikation 05.12.2024 | …/eCH-0276/1 | "0" | 890 |
eCH-0276-1-0-4_20260115.xsd (032a3449…) | aus dem swissdec-Transmitter-Paket, Ausgabe 06.03.2026; Dateistand 15.01.2026 | …/eCH-0276/1 | "0" | 892 |
eCH-0276-2-0-0.xsd (df4cd47a…) | Beilage zum eCH-Entwurf V2.0.0, Publikation 23.07.2026 | …/eCH-0276/2 | "0" | 934 |
Die mittlere Datei ist wichtig genug, um sie zweimal zu benennen: Sie ist nicht auf ech.ch publiziert. Sie liegt im Ordner schema/ des swissdec-Pakets vom 06.03.2026, und der Container dieses Pakets bindet sie namentlich: <xs:import namespace="http://www.ech.ch/xmlns/eCH-0276/1" schemaLocation="eCH-0276-1-0-4_20260115.xsd"/>. Wer über den Distributor überträgt, validiert also gegen eine andere Datei als die, die eCH unter derselben Versionsnummer veröffentlicht. Das ist keine Ambiguität, sondern eine Bindung, und sie steht in einer Zeile einer einzigen Datei — man findet sie nur, wenn man das Paket auspackt.
Und eine Korrektur an einer Formulierung, die man oft liest, auch von uns: Die beiden Dateien tragen nicht «dieselbe Versionsangabe 1.0». Das version-Attribut im xs:schema-Tag lautet in allen drei Dateien "0". Der Standard erklärt das selbst, im Hauptdokument §2.4: «Die eCH-Standard Major-Version, die dem XML zugrunde liegt ist im Namespace aufgeführt» — und: «Die eCH-Standard Minor-Version wird in jedem Fall im XML-Schema im xs:schema Tag mittels „version = X" gesetzt.» Die publizierte und die swissdec-Datei teilen sich also den Namensraum http://www.ech.ch/xmlns/eCH-0276/1, die Schema-Versionsangabe version="0" und dieselbe Pflicht-Attributdeklaration minorVersion. Ein Validator, der zwischen ihnen unterscheiden wollte, hat nichts, woran er das täte.
Der Namensdiff, und warum Tippfehlerreparaturen brechen
Zwischen der publizierten und der swissdec-Datei sind 13 Elementnamen entfernt und 15 neu. Entfernt werden unter anderem annualProfitOrAnnualLoss, disclosedHiddenReserves, participationInPercent, primeCostsNumber, freeCapitalReservesTaxLaw und tiedCapitalFreeFundsTaxLaw; neu sind unter anderem disclosedHiddenReservesInformativ, disclosedHiddenReservesTaxableCapital, generalParticipationsDetails, balanceSheetDateBusinessYear und unpaidShareCapitalTaxableEquity.
Darunter sind reine Schreibkorrekturen: incomTaxValueOfTotalAssetsAtTheEndOfTheTaxPeriod heisst neu incomeTaxValueOfTotalAssetsAtTheEndOfTheTaxPeriod; otherReasontaxAdjustmentsDecreasingTaxableResult — mit kleinem t — verschwindet ersatzlos. Eine Umbenennung, die einen Tippfehler heilt, bricht jede konforme Instanz genau so hart wie eine inhaltliche Änderung. Der Parser kennt keine Absichten; er kennt Zeichenketten.
Darunter ist aber auch keine Reparatur, sondern eine Neuerung: taxExemptLegalEntity, Typ xs:boolean. Eine neue erklärbare Tatsache über den Steuerpflichtigen — steuerbefreit ja oder nein —, eingeführt innerhalb einer unveränderten Hauptversion, in einer Datei, die ein Anbieter aus einem Übermittlungspaket bezieht und nicht von der Normierungsstelle.
Der eigentliche Fund, den der Namensdiff verfehlt
Namen kann man mit dem Auge vergleichen. Typen nicht. 88 der 877 gemeinsamen Elementnamen wechseln zwischen den beiden Dateien still den Datentyp; in 84 Fällen von xs:long nach xs:decimal. Es trifft die Bilanz und die Erfolgsrechnung im Kern: totalAssets, totalEquity, cashAndCashEquivalents, machinery, goodwill, currentProvisions, accruedExpenses, fixedTangibleAssets.
Das ist konkret und falsifizierbar, und wir haben es nicht behauptet, sondern gefahren. Aus beiden Originaldateien je eine lokale Kopie mit einem Testwurzelelement vom Typ assetsType, dieselbe Instanz gegen beide, xmllint --schema:
<totalAssets>1234567.89</totalAssets>
gegen die ech.ch-Datei: Element 'totalAssets': '1234567.89' is not a
valid value of the atomic type 'xs:long'. → fails
gegen die swissdec-Datei: validates
Gleicher Namensraum. Gleiche Versionsangabe. Rappen sind in der einen Datei ein Fehler und in der anderen erlaubt, und nichts in den Dateien sagt, welche man gerade hat.
Für den Bau heisst das zweierlei, und beide Richtungen sind unangenehm. Wer gegen die publizierte Datei entwickelt, rundet die Rappen weg, bevor er schreibt — korrekt gegenüber diesem Schema, aber gegenüber der swissdec-Datei ein stiller Genauigkeitsverlust, den niemand meldet, weil eine gerundete Zahl auch dort gültig ist. Wer umgekehrt gegen die swissdec-Datei entwickelt und Rappen liefert, erzeugt eine Datei, die gegen die publizierte Fassung an 84 Stellen bricht — und zwar sichtbar. Ein Umbenennen fällt bei der ersten Validierung auf. Ein Typwechsel fällt nur auf, wenn man beide Schemata besitzt und beide fährt.
V1 → V2: die Verschiebung, die kein Änderungsverzeichnis führt
Von der publizierten V1.0.0 zum V2.0.0-Entwurf: 751 gemeinsame Namen, 139 entfernt, 183 neu — und 225 der 751 gemeinsamen Namen wechseln den Datentyp. 149 davon von xs:long nach twoDecimalDigitsType, 28 nach taxAmountType. Der neue Typ ist definiert als
<xs:simpleType name="twoDecimalDigitsType">
<xs:restriction base="xs:decimal">
<xs:fractionDigits value="2"/>
</xs:restriction>
</xs:simpleType>
— zwei Nachkommastellen, und keine Grenzen. Mit xs:long verschwindet auch dessen Obergrenze von 9'223'372'036'854'775'807. Praktisch wird das keine Jahresrechnung sprengen; dokumentiert ist es unterschiedlich: die Wertebereichsgrenze entfällt ohne eigenen Eintrag, während dasselbe Änderungsverzeichnis die alphabetische Sortierung einer Übersichtstabelle als Änderung ausweist.
Die Bewegung ist auch nicht einheitlich gerichtet. In derselben Fassung wird Typsicherheit an einer Stelle gewonnen und an einer anderen aufgegeben. Gewonnen: country geht von xs:string auf eCH-0010:countryType, cantonOfBusinessPremises von xs:string auf eCH-0007:cantonAbbreviationType. Aufgegeben: zipCodeHeadOffice geht von eCH-0010:swissZipCodeType — vier Ziffern, geprüft — auf eCH-0276:textField254Type, also 254 beliebige Zeichen; townHeadOffice denselben Weg. Land und Kanton werden enger, Postleitzahl und Ort werden zu Freitext. Das ist kein Versehen, das man von aussen diagnostizieren könnte, und es ist auch keine Kritik — es ist eine Beobachtung, die jeder mit den beiden Dateien in zwei Minuten reproduziert. Aber wer in seiner Ausgangsprüfung auf swissZipCodeType vertraut hat, verliert diese Prüfung beim Versionswechsel, ohne dass ihm jemand davon abrät.
Anhang D des V2.0.0-Hauptdokuments, «Änderungen gegenüber Vorversion», führt fünf Zeilen: SimpleType-Überblick alphabetisch sortiert, Referenzen auf Titelseite und in Anhang A angepasst, Abbildungen aktualisiert, Titel für Anhänge hinzugefügt, Namenskonventionen zu XML-Datei und Paket ergänzt. Keine der 225 Typänderungen steht darin. Das Änderungsverzeichnis beschreibt das Dokument; die brechenden Änderungen liegen in der Beilage.
Eine Zahl als Leitfaden durch die ganze Drift
annualProfitOrAnnualLoss, der Jahresgewinn, zeigt die Bewegung an einem einzigen Wert.
- V1.0.0: ein Name, zwei Deklarationsorte — auf der Passivseite
minOccurs="0", in der ErfolgsrechnungminOccurs="1". Beidexs:long. - swissdec 1-0-4: zwei Namen.
annualProfitOrAnnualLossEquityAndLiabilities(xs:decimal, optional) undannualProfitOrAnnualLossIncomeStatement(xs:long, obligatorisch). - V2.0.0-Entwurf: drei Namen über drei Register. Die beiden Rechnungsvarianten sind neu
twoDecimalDigitsTypeund beide optional. Der einzige obligatorische heisstannualProfitOrAnnualLossFiscalCorrections, liegt in den steuerlichen Korrekturen und bleibt als einziger Ganzzahl.
Geschlossen wird das von der Regelwerkzeile D9200 der SSK, Aktion: «= C9200, gerundet auf Ganzzahl», mit dem Zusatz «Die Art der Rundung wird durch Kanton festgelegt.»
Eine Zahl, vier Typisierungen, drei Dateien, die alle eCH-0276 heissen. Die Rechnungszahl ist präzise und optional; die Steuerzahl ist gerundet und Pflicht. Wer wissen will, wo in diesem Standard die Buchhaltung endet und die Veranlagung beginnt, findet die Grenze an dieser Stelle, im Datentyp.
Migration, getrieben statt behauptet
Beide offiziellen Beispielinstanzen validieren sauber gegen ihr eigenes Schema — die Prüfkette trägt also. Dann die naheliegende Abkürzung: die V1-Instanz nehmen, s/eCH-0276\/1/eCH-0276\/2/g, gegen V2 validieren. Ergebnis: Fehler an mindestens acht Stellen, wörtlich:
'statuatoryCapitalReserveLimitedCompanies': This element is not expected.
Expected is one of ( … statutoryCapitalReserveLimitedCompanies … )
'TotalOtherOperatingExpenses': This element is not expected.
Expected is ( totalOtherOperatingExpenses )
'profitOrLossBroughtForward': Character content other than whitespace is not
allowed because the content type is 'element-only'
Zwei Tippfehlerreparaturen und ein Skalar, der zum komplexen Typ geworden ist — in genau den Meldungen, die der Migrierende sehen wird. Den Namensraum umzuschreiben ist keine Migration.
Das TestCase-Loch
Die Richtlinien verlangen ein Testkennzeichen zweimal. §8.1, alternativer Ablauf: «Die Meldung wird als Testmeldung gekennzeichnet. (Ein Element TestCase wird im Job Element eingefügt).» §8.3, UC003: «Das TestCase Flag ist jeweils auf dem Job bei der initialen Meldung oder im CaseContext bei der Synchronisation gesetzt», Nachbedingung: «Die Meldung wurde vom Endreceiver empfangen. Sie darf nicht produktiv weiterverarbeitet werden.» Anhang F, AB-11, Anforderung 4: «Jede Meldung, die in Zusammenhang mit dem Geschäftsvorfall versendet wird, muss die Testfall-Markierung tragen.»
Gemessen am 04.08.2026 über das Paket vom 06.03.2026: grep -r "TestCase" über alle 35 mitgelieferten XSD → 0. Über alle 11 mitgelieferten SOAP-Beispiele → 0. Testfall über schema/ → 0.
Es ist auch kein Einbauort offen. DeclareBalanceSheetJobType und DeclareRawBalanceSheetJobType sind geschlossene xs:sequence mit je zwei Kindern — Addressees beziehungsweise Addressee, dazu ein optionales Substitution —, und xs:any kommt im Container-Schema null mal vor. Eine Nachricht mit einem TestCase-Kind wäre schema-ungültig; eine schema-gültige Nachricht kann keines tragen.
Die Grenze dieses Befunds gehört dazu: Anhang A referenziert ein [CONTAINERXSD] Container Schema. Swissdec. ohne URL, und dieses Schema liegt dem Paket nicht bei. Und der Befund lautet «0 über die Schemata und die Beispiele», nicht «0 über das ganze Paket» — die PDF-Ströme sind komprimiert, im deutschen Fliesstext steht TestCase zweimal, an den beiden oben zitierten Stellen.
Und dann der Satz, der aus unserem eigenen Protokoll stammt und über niemanden urteilt: Wir kennen diese Konstellation. In Deutschland haben wir sechs Erklärungen verloren, weil ein fehlendes Testmerkmal nicht als Fehler galt, sondern als Echtfall. Ein Testkennzeichen, das die Spezifikation verlangt und das Schema nicht kennt, hat genau eine Ausfallrichtung: alles ist produktiv. Wer diesen Kanal implementiert, sollte das vor dem ersten Aufruf klären — nicht danach.
Eine offene Spannung um UC002
§3.2 der Richtlinien sagt: «Die Use Cases Abschnitt 8.2, ‹UC002 Status abholen› und Abschnitt 8.12, ‹UC012 Dialog Message abwickeln› werden von eBilanz nicht unterstützt.» Zugleich bildet Tabelle 3.1 UC002 auf konkrete Operationen ab (GetStatusFromDeclareBalanceSheet und die zugehörige Response), spezifizieren §§7.6 und 7.7 sie vollständig, bindet die WSDL sie als SOAP-Operation, liegen zwei Beispiel-XML dafür bei, und §8.1 führt UC002 unter den Included Use Cases der initialen Meldung. §7.2 heisst «Initiale asynchrone Meldung» — und asynchron heisst in §8.1 ausdrücklich: «Asynchroner Aufruf, bei dem der Distributor mit einem JobKey antwortet und der Transmitter mittels <GetStatus> Polling die Antworten der Endreceiver erhält.» Der eCH-0276-Weg, also genau der Weg, den eine Deklarationslösung geht, ist ohne Statusabfrage nicht abschliessbar. Wir stellen das als offene Spannung dar und lösen sie nicht auf — beide Sätze stehen in demselben, 126 Seiten langen Dokument, und keiner der beiden ist ein Tippfehler. Wer den Kanal implementiert, sollte vor der Zertifizierung wissen, welche der beiden Aussagen gilt, denn UC002 ist die eine Operation, die eine private Deklarationslösung in jedem Fall bauen müsste.
Die kleineren Sachen, alle prüfbar
Anhang G rendert die Nutzlastzeile beider Operationen als <null>bsc:blubber</null>, während die XSD korrekt eCH-0276:eBalanceSheetETaxLegalEntity bzw. xbrl:xbrl referenziert; grep -rn "blubber" schema/ ergibt 0. Die eine Zeile, die die zwei Spuren unterscheidet, ist im menschenlesbaren Anhang ein Platzhalter.
Namensraum-Grossschreibung: Die Hauptdokumente V1 und V2 schreiben in §2.4 http://www.ech.ch/xmlns/ech-0276/1 bzw. …/ech-0276/2, alle drei Schemata eCH-0276. URIs sind zeichengenau. Wer der Prosa folgt statt der XSD, erzeugt eine Instanz, die nicht validiert.
Fünf Enum-Schreibweisen stehen im V2-Hauptdokument, die die V2-XSD ablehnt: bookprofitsFromHigherValuation…, …OfPeridiodicity (zweimal), othertypeOfSecurity, singleFamilyHousesflat…. Gegenprobe: In V1 stimmen Dokument und Schema an allen fünf Stellen überein — beide tragen die alte Schreibweise. Die V2-XSD hat repariert, die V2-Prosa nicht. Das ist eine Regression des Konsultationsentwurfs, keine Alteigenschaft des Standards.
attachments trägt in V2 minOccurs="2" — das einzige minOccurs="2" im ganzen, 2'226 Zeilen langen Schema — bei einem selbstrekursiven Typ. Das offizielle Referenzbeispiel emittiert den Block deshalb zweimal byte-identisch, mit leerem <documentTitle/>.
Der Fehler, den man vor dem 03.09.2026 melden kann und der fünf Zeichen kostet
textFieldAttachmentTitleType, neu in V2 und Typ von documentTitle:
<xs:pattern value="[a-zA-Z0-9äöüÄÖÜ.\-_ ]*"/>
Deutsche Umlaute — aber kein Akzent, kein Apostroph, kein Komma, keine Klammer. «Bericht der Revisionsstelle, 2025» ist ungültig (Komma). «Rapport de l'organe de révision» ist ungültig, zweifach: Apostroph und é. In einem Standard, dessen Beilagen d_f heissen. Wenn dieser Text eine Sache in die Vernehmlassung einbringt, dann diese. Die Vernehmlassung läuft über ein öffentliches Formular mit einem Knopf «RFC erstellen», ohne Mitgliedschaftsschranke, bis zum 3. September 2026.
Der Governance-Widerspruch, der die ganze Drift legitimiert
Warum darf sich ein genehmigter Standard so bewegen? Weil er es sich selbst erlaubt — und es zugleich bestreitet. Zwei Klauseln, zwanzig Seiten auseinander, im selben Dokument:
§1.1: «Genehmigt: Das Dokument wurde vom Expertenausschuss genehmigt. Es hat für das definierte Einsatzgebiet im festgelegten Gültigkeitsbereich normative Kraft.»
§4: «eCH-Standards … haben nur den Status von Empfehlungen. … Der Inhalt von eCH-Standards kann jederzeit und ohne Ankündigung geändert werden.»
Dazu, aus einem Kanton, eine dritte Beschreibung desselben Rechtszustands — Protokoll des Steueramts Solothurn mit EXPERTsuisse und TREUHAND|SUISSE vom 25.11.2025, Traktandum 8: «Der Standard ‹eCH-0276› wurde per 01. Januar 2025 in Kraft gesetzt. Gegenwärtig sind noch Anpassungen und Korrekturen am Standard in Arbeit.»
Normative Kraft, blosse Empfehlung, in Kraft gesetzt. Drei Beschreibungen, alle publiziert, alle datiert. Wir stellen sie nebeneinander und vereinfachen sie nicht — die frühere Formulierung, eCH-Standards seien «ihrer eigenen Bestimmung nach Empfehlungen», gibt nur eine der drei wieder.
Die Empfehlung, mit swissdecs eigener Autorität dahinter
Sie steht auf Seite iv der Transmitter-Richtlinien, vor allen Diagrammen:
«Verbindlich sind ausschliesslich die jeweils veröffentlichten offiziellen XML-Schemata.»
Daraus folgt die einzige Regel, die dieses Kapitel praktisch macht: Gegen das Schema validieren, nicht gegen die Versionsnummer — und beim Bezug notieren, aus welchem Paket die Datei stammt. Eine Versionsnummer beantwortet in diesem Standard heute nicht, welche Datei gemeint ist. Ein SHA-256 und ein Paketdatum beantworten es.
Wer den Kanal baut, kann daraus eine Kurzliste machen. Erstens: jede bezogene XSD hashen und den Bezugsort mitschreiben, nicht nur die Version. Zweitens: die Regressionsprüfung gegen alle im Umlauf befindlichen Fassungen fahren, nicht gegen die neueste — der Unterschied zwischen 890, 892 und 934 Elementnamen ist keine Kosmetik, und die 88 beziehungsweise 225 Typwechsel sieht nur, wer sie misst. Drittens: vor dem ersten produktiven Aufruf schriftlich klären, wie eine Testmeldung auf diesem Kanal gekennzeichnet wird und was der Endempfänger tut, wenn die Kennzeichnung fehlt. Die Richtlinien beschreiben in AB-12 drei Prüfungsstufen — «Validierung, Plausibilisierung, Akzeptierung» — und halten fest: «Nur der Endreceiver kann die Akzeptanzstufe durchführen.» Wer den ersten Aufruf absetzt, ohne diese Frage geklärt zu haben, erfährt die Antwort an einer echten Steuererklärung.