Erster Stand des öffentlichen Ausschnitts aus dem Vault. Die .gitignore arbeitet als Positivliste: ausgeschlossen ist alles, eingeschlossen nur, was ausdrücklich wieder aufgenommen wird. Notizen, Projekte, Finanzen, das Sitzungsgedächtnis und settings.local.json bleiben damit außen vor. Der Kundenname in AI/Regelwerk/seo.md ist durch eine neutrale Bezeichnung ersetzt; die Regeln selbst sind unverändert. Co-Authored-By: Claude Opus 5 <[email protected]>
133 lines
12 KiB
Markdown
133 lines
12 KiB
Markdown
---
|
|
type: resource
|
|
status: pending-review
|
|
title: Prüfwerkzeuge
|
|
summary: 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.
|
|
tags: [regelwerk, pruefwerkzeuge, code-projekte]
|
|
created: 2026-08-19
|
|
updated: 2026-09-09
|
|
---
|
|
|
|
# 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|codeworkflow.md]], „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
|
|
|
|
- [[AI/Regelwerk/codeworkflow|↑ Codeworkflow]]
|
|
- [[CLAUDE|↑ Claude.md (Stamm)]]
|