Files
Pascal MichelandClaude Opus 5 b8b71f04fd Automatischer Push: AI/Regelwerk/pruefwerkzeuge.md
- AI/Regelwerk/pruefwerkzeuge.md

Erzeugt vom PostToolUse-Hook .vault-tools/auto-push/auto_push.py.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-17 23:05:40 +02:00

12 KiB

type, status, title, summary, tags, created, updated
type status title summary tags created updated
resource reference Prüfwerkzeuge Regeln für mechanische Prüfungen und Prüfskripte — Entwurf vor Code, Herkunft jeder Aussage, vollständige Angriffsliste, keine Prüfung die sich selbst abschaltet, Abbruchbedingung.
regelwerk
pruefwerkzeuge
code-projekte
2026-08-19 2026-09-17

Prüfwerkzeuge

Gilt, sobald ein Skript entsteht, das etwas beurteilt statt etwas zu tun: Konventionsprüfung, Rechteprüfung, Schema- oder Konfigurationsprüfung, Gate-Erweiterung. Nicht für gewöhnliche Testsuiten.

Der Anlass für diese Datei: Im Projekt „Logging" hat eine einzige Prüfung drei Fix-Runden und drei unabhängige Re-Reviews gebraucht, davor eine andere fünf blinde Fassungen. Jede Runde fand echte Fehler — aber fast alle wären in einer einzigen Runde gefunden worden, wenn die folgenden vier Schritte am Anfang gestanden hätten statt am Ende.

1 Entwurf vor Code: die Entscheidungsliste

Bevor eine Zeile entsteht: Schreib auf, welche Fragen die Prüfung beantworten soll. Je Frage eine Zeile, und je Zeile die Herkunft der Antwort. Es gibt genau drei:

Herkunft Bedeutung Pflicht
Quelle Steht im Quelltext, Schema oder in der Referenzdoku des fremden Systems Abschreiben, Fundstelle nennen, maschinell gegenprüfbar halten
Projektvorgabe Eine Entscheidung, die wir getroffen haben Muss im Spec stehen, nicht nur im Skript
Annahme Weder das eine noch das andere Auflösen, bevor Code entsteht

Die dritte Zeile ist die gefährliche. Eine Annahme wird entweder zur Projektvorgabe erhoben und im Skript erzwungen — oder die Prüfung beantwortet die Frage nicht.

Der Beleg, warum das keine Formalie ist. Eine Aufbewahrungsprüfung unterstellte stillschweigend, es gebe genau einen Warm- und genau einen Löschzustand. Diese Annahme stand nirgends. Sie machte die Prüfung zur Heuristik im Positivlisten-Gewand: ein zweiter Löschpfad mit kürzerer Frist passierte mit Exit-Code 0 und ohne eine einzige Meldung — Daten wären nach einem statt nach dreißig Tagen gelöscht worden. Erst als die Form zur erzwungenen Projektvorgabe wurde, war der Fehler konstruktiv ausgeschlossen statt nur zugeklebt.

Die Begründung darf nie breiter sein als ihr Geltungsbereich. „Die erlaubte Menge ist geschlossen" galt für Feldnamen und Invarianten aus dem Quelltext — nicht für die Auswahl der erlaubten Aktionen, nicht für die zugelassene Musterform, nicht für die Graphform. Wer alles unter dieselbe Begründung stellt, hält die schwächste Prüfung für so belastbar wie die stärkste. Der Kopfkommentar trennt die Achsen ausdrücklich.

2 Abschreiben statt rekonstruieren

Steht die erlaubte Menge im fremden Quelltext: Datei ziehen, Liste herauskopieren, Fundstelle in den Kopfkommentar. Nicht aus dem Gedächtnis, auch nicht „ich weiß das".

Belege aus einer Sitzung, alle derselbe Mechanismus:

  • Feldlisten aus dem Gedächtnis gebaut — drei Felder fehlten, elf Invarianten waren gar nicht übernommen.
  • Eine Liste verbotener Zeichen enthielt eines, das ich geraten hatte.
  • Beim Nachzählen verwendeter Aktionsgruppen kam mein eigenes Prüfskript auf fünf statt sieben, weil es einen Abschnitt der Datei nicht auswertete.

Und die schärfere Regel dazu: Wenn du es nicht gezogen hast, schreib nicht, dass du es gezogen hast. Eine falsche Zusage von Quellentreue ist schlimmer als gar keine — sie hält die nächste Prüfung davon ab, hinzusehen. Was du nicht belegt hast, lässt du weg und benennst die Lücke im Kopfkommentar. Eine benannte Lücke ist ein Ergebnis, eine geratene Liste ist eine Falle.

3 Die Angriffsliste — einmal vollständig statt dreimal nachgereicht

Ein grüner Lauf über gesunde Daten belegt nichts. Die Frage ist ausschließlich, ob die Prüfung einen echten Verstoß findet. Deshalb wird je geprüftem Feld und je geprüfter Struktur diese Taxonomie einmal komplett durchgegangen:

Klasse Beispiel
fehlt Pflichtfeld nicht vorhanden
null ausdrücklich null statt eines Wertes
falscher Typ Liste statt Objekt, Zahl statt Liste, Wahrheitswert statt Zahl
leer leere Liste, leeres Verzeichnis, leere Datei
doppelt zwei Einträge mit demselben Namen
zusätzlich unbekanntes Feld, zweite Aktion, zweiter Pfad
Wertebereich 0, -1, der Grenzwert selbst
Verweis ins Leere ein Name zeigt auf ein Ziel, das es nicht gibt
Reihenfolge derselbe Schaden an erster und an letzter Listenposition
Umgebung Zielverzeichnis fehlt, Referenzdatei fehlt, Ausgabekodierung, fehlender Interpreter

Die Zeile „Reihenfolge" ist die, deren Fehlen am teuersten war. Der schwerste Fund einer ganzen Sitzung wurde nur erkannt, wenn der Schaden zufällig am Listenende stand — an Position zwei war er unsichtbar. Wer einen Fall baut, baut ihn an zwei Positionen.

Jeder Fall wird einzeln gefahren, auf einer Wegwerfkopie, mit Rücksetzen dazwischen. Ein Treiber, der alle Fälle abarbeitet und je Fall Exit-Code und auslösende Meldung protokolliert, ist die Mindestform. Und der Treiber prüft zuerst die Grundlinie: läuft die unbeschädigte Kopie nicht sauber durch, misst der Rest nichts.

4 Das Skript darf nicht abstürzen

Ein Traceback ist ein Befund, kein Ergebnis. Das Gate wertet den Exit-Code, und ein Absturz liefert ebenfalls „ungleich null" — nur ohne verwertbare Meldung. Deshalb:

  • Jede Ebene bekommt eine Typprüfung, bevor auf sie zugegriffen wird.
  • Der Hauptablauf läuft in try, ein unerwarteter Abbruch wird selbst als Befund gemeldet.
  • Die gesammelten Befunde werden ausgegeben, auch wenn abgebrochen wurde. Sonst sieht der Anwender den Absturz statt der Ursache, die längst in der Liste stand.

5 Neuer Code in einer Fix-Runde bekommt seinen eigenen Fall

Die Nachbesserung, die einen blockierenden Befund behebt, ist selbst neuer, ungeprüfter Code. In derselben Runde kommt ein Angriffsfall dazu, der genau ihn trifft. Beleg: Die Formprüfung, die den Datenverlustpfad schloss, hatte in ihrer ersten Fassung einen Absturz bei einem Tippfehler im wahrscheinlichsten Feld.

6 Wenn die Prüfung ans Gate kommt

Kein Wächter, der den Schadensfall ausblendet. Ein if [ -d ism ] vor dem Aufruf machte das Löschen des gesamten Verzeichnisses zum stillen Durchlass — den kürzesten Weg, die Sache stillzulegen, sah das Gate als einziges nicht. Das Skript meldet den Fall selbst besser, als der Wächter ihn verschweigt.

Keine Prüfung, die sich selbst abschaltet. Die innere Variante desselben Fehlers, und die gefährlichere, weil sie im Skript steht statt drumherum. Beleg vom 2026-08-27 im Projekt „Authentik": Die Prüfung auf erfundene Normkennungen las eine Referenzliste und kehrte bei fehlender Liste stillschweigend zurück. Fehlte die Datei, meldete sie nichts — und das Werkzeug schrieb daraufhin „Keine der sieben Prüfungen hat angeschlagen" und gab Exit-Code 0 zurück. Es behauptete also, alle sieben seien gelaufen, während eine gar nichts getan hatte.

Die Regel dagegen ist knapp: Eine fehlende Voraussetzung ist ein Befund, kein Grund zu schweigen. Wenn eine Prüfung ohne Referenzdatei, ohne Datenbankverbindung oder ohne Netzzugang nicht arbeiten kann, meldet sie genau das. Sonst wird der einzige Zustand, in dem sie nutzlos ist, zugleich der Zustand, in dem sie grün meldet.

Zu unterscheiden davon ist der Fall „nichts zu prüfen": Beansprucht kein einziges Dokument eine Kennung, braucht es auch keine Referenzliste, und Schweigen ist richtig. Die Frage lautet deshalb nicht „ist die Voraussetzung da", sondern „brauche ich sie für das, was hier tatsächlich zu prüfen wäre".

Der Prüfumfang wird an einer Stelle geführt. Wo im Spec oder in der Dokumentation aufgezählt ist, was das Gate prüft, wird diese Aufzählung mitgezogen. Sonst driftet sie — dreimal in einer Sitzung passiert. Aus demselben Grund wird eine Zahl im Schlusstext (»keine der sieben Prüfungen«) gerechnet und nicht geschrieben.

7 Abbruchbedingung, vor der ersten Runde benannt

Prüfrunden haben eine Ertragskurve. Die aus dem Logging-Projekt, als Maßstab:

Runde Ertrag
1 ein konzeptioneller Kernbefund (Listen rekonstruiert statt abgeschrieben)
2 ein blockierender Datenverlustpfad
3 ein Absturz in neuem Code plus eine Formulierungsfrage

Schluss ist, wenn eine Runde nur noch Umformulierungen findet oder wenn die verbleibenden Befunde alle dieselbe Antwort haben („übernehmen oder als ausgelassen benennen"). Das ist eine Aufräumliste, kein Prüfauftrag.

Frag den Prüfer im Abschlussauftrag ausdrücklich: hat eine weitere Runde noch Substanz, oder schichten wir nur noch Text um? Er hat darauf zweimal brauchbar geantwortet und einmal von sich aus empfohlen, ohne weitere Runde zu committen.

Und der Gegenpol: Der belastbare Nachweis liegt fast nie im Skript. Er liegt in der Zielumgebung. Ein Skript ist ein Fangnetz gegen offensichtliche Fehler — das gehört in den Kopfkommentar und darf an keiner Stelle des Projekts als Nachweis geführt werden.

8 Prüfungen, die Quelltext lesen, messen zuerst ihre eigenen Kommentare

Eine ganze Klasse von Prüfungen sucht nicht in Daten, sondern im Quelltext des geprüften Projekts: „dieser Aufruf darf nicht mehr vorkommen", „dieser Container muss geprüft werden", „hier steht kein Hex-Literal". Sie sind billig und deshalb verlockend. Sie haben aber eine Eigenart, die keine Datenprüfung hat: der erklärende Kommentar über der Codestelle enthält fast immer genau die Zeichenfolge, um die es geht — er hält ja die Fallgeschichte fest.

Was daraus folgt, ist nicht nur ein falscher Befund. Es ist ein Zwang in die falsche Richtung: Wer die Prüfung grün bekommen will, formuliert den Kommentar um und löscht damit die Begründung, die der eigentliche Wert der Stelle war.

Am 2026-09-09 ist das im Projekt „RootCA - EnterpriseCA" viermal in einer Sitzung passiert, dreimal davon in Prüfungen, die ich selbst in denselben Plan geschrieben hatte:

  • Eine Prüfung verbot einen auditpol-Aufruf im gesamten Skripttext — der vorgeschriebene Kommentar über der neuen Aufrufstelle zitierte ihn absichtlich.
  • Eine Prüfung verlangte eine Trefferzahl von zwei für eine GUID, während dieselbe Vorgabe sie ausdrücklich einmal als Konstante anlegte.
  • Eine Prüfung verlangte, dass Containername und Funktionsaufruf in derselben Zeile stehen, und hätte damit den Zeilenumbruch des Codes vorgeschrieben.
  • Eine Prüfung las den Rumpf einer Funktion über den AST — der Extent.Text enthält die Kommentare, und der Kommentar zählte alle drei gesuchten Container auf. Das Löschen einer der drei Codezeilen blieb grün.

Die ersten beiden hat der Executer gemeldet statt sie stillschweigend aufzulösen; die dritte ebenfalls. Die vierte fiel erst im Mutationslauf auf — beim Lesen war sie unauffällig.

Daraus drei Regeln:

  1. Kommentarzeilen ausfiltern, bevor gesucht wird. Auch bei einem Funktionsrumpf aus dem AST: Extent.Text trägt die Kommentare mit.
  2. Die gemeinte Eigenschaft prüfen, nicht eine Trefferzahl. „Jeder Aufruf holt seinen Parameter aus einer Variablen" überlebt jede Umstrukturierung; „die GUID kommt zweimal vor" muss bei jeder Änderung von Hand nachgezogen werden und wird dabei falsch (siehe auch AI/Regelwerk/codeworkflow, „Keine gepflegten Zahlen in Erwartungstexten").
  3. Nichts an einer Textprüfung glauben, bevor sie mit gelöschter Zielzeile rot gesehen wurde. Das ist derselbe Regressionsnachweis wie überall — er ist hier nur besonders billig und wird deshalb besonders oft ausgelassen.

Und ein Mutant, der nichts ändert, ist keine Lücke. Im selben Lauf blieb der Tausch zweier gleichbedeutender Vergleichsoperatoren grün. Das sah nach einem blinden Fleck aus, war aber ein wirkungsloser Mutant. Bevor ein durchgerutschter Fall als Testlücke gemeldet wird: prüfen, ob er das Verhalten überhaupt verändert — und den Fehler, der tatsächlich passiert ist, in seiner exakten Form nachstellen, nicht nur seine Einzelteile.

Referenzen