Deterministische Quality Gates
Wie Feedback-Schleifen vom Language Server bis zur CI das Verhalten von Coding-Agents begrenzen.
Verfasst von
-
Benedikt Stemmildt Gründer & Co-CEO
Aktualisiert:
Agentic Engineering 5
Wie Feedback-Schleifen vom Language Server bis zur CI das Verhalten von Coding-Agents begrenzen.
Das Latenzspektrum der Feedback-Schleifen und deterministische Gates
Unit-Tests sind nur eine von mehreren Feedback-Schleifen. Der Grundgedanke: Umgebt den Agenten mit überlappenden Schleifen und ordnet sie nach Latenz. Bewertet jede Schleife dabei auf zwei Achsen statt auf einer. Wie schnell liefert sie ein Urteil, auf das der Agent reagieren kann? Und wie viel beweist dieses Urteil? Die beiden Achsen ziehen gegeneinander. CI ist die langsamste Schleife und beweist am meisten, ein Linter antwortet fast sofort und beweist am wenigsten. Die Ordnung nach Latenz sagt euch, wann eine Schleife greift, aber nicht, welche wichtiger ist. Das schnelle Ende ist also nicht automatisch das wertvolle. Hier wirkt das Prinzip Fast Feedback auf einen Agenten: Je früher ein Fehler auftaucht, desto billiger lässt er sich beheben. Einen Fehler, den der Agent nach Millisekunden sieht, behebt er weit günstiger als einen, den ein Mensch Tage später im Review findet.
Am schnellsten urteilt der Language Server. Er markiert Typfehler und falsche Signaturen, sobald der Agent tippt, noch vor jedem Compile. Der Compiler fängt, was erst ein vollständiger Build zeigt. Der Linter prüft beim Speichern Stil und einfache Korrektheit. Die Testsuite ist die Schleife für Verhalten und braucht Sekunden pro Lauf. Als letztes Gate fährt CI beim Push die volle Suite plus Integrations- und Security-Scans. Jede Schleife fängt eine andere Fehlerklasse, und jeder früh gefangene Fehler erreicht die nächste Schleife nicht mehr.
Strikte, statische Typsysteme sind ein starker Default: TypeScript, Rust, Go oder typisiertes Python über mypy oder pyright. Sie geben dem Agenten eine Prüfung, die greift, bevor der Code läuft. Der Type Checker arbeitet als unermüdlicher Reviewer. Er meldet eine halluzinierte Property oder ein unbehandeltes Null genau in der Zeile. Liest der Agent diese Diagnose, behebt er den Fehler, bevor ein Mensch ihn sieht. Untypisiertes Python lädt dagegen offen dazu ein, Datenstrukturen zu halluzinieren. Legt die Form deshalb fest, mit einem TypedDict (statisch geprüft) oder einem Pydantic-Modell (zur Laufzeit validiert). Linter und Formatter lassen den Agenten an unnachgiebigen Wänden abprallen, bis sein Output modularer und besser benannt ist. Das ist derselbe Mechanismus wie oben beschrieben.
Die Instruktion, den Linter laufen zu lassen, steht im Prompt. Ein nicht-deterministischer Agent vergisst sie manchmal, überspringt sie bei langem Kontext oder hält den Code einfach für in Ordnung. Eine Instruktion, die der Agent ignorieren kann, ist keine Garantie. Die Lösung: Bindet die Prüfung an ein Ereignis, damit sie unabhängig von der Entscheidung des Agenten greift. Das leisten zwei Ebenen:
- Git-Hooks (
pre-commit,pre-push) sichern den Commit selbst ab und funktionieren mit jedem Tool. Sie fahren Formatter, Linter und schnelle Tests, egal wer oder was die Änderung gemacht hat. Das gilt aber nur, wenn sie in einem getrackten Verzeichnis versioniert und übercore.hooksPathoder ein Framework eingebunden sind..git/hooksreist nämlich nicht mit dem Repository. - Agent-Hooks (
pre-tool-use,post-edit) liegen im Setup des Agenten selbst. Sie prüfen seine Aktionen schon während des Editierens, also noch früher.
Beide Ebenen nehmen die Prüfung aus dem Ermessen des Agenten, und genau darum geht es. Ein entschlossener Aufrufer umgeht einen Git-Hook trotzdem mit --no-verify. Um diese letzte Lücke zu schließen, sperrt ihr die Gates gegen den Agenten, der sie passieren will. Verbietet Änderungen an der Lint- und Coverage-Konfiguration, schützt den Main-Branch und blockiert den Ausweg über --no-verify. Denn welche Regel ihr auch setzt, der Agent sucht einen Weg drumherum (Code Quality Foundations for AI-assisted Codebases (öffnet in einem neuen Tab), 2026). Thoughtworks führt denselben Schritt unter dem Namen “feedback sensors for coding agents”. Gemeint sind deterministische Gates wie Compiler, Linter und strukturelle Tests, eingebunden in den agentischen Loop. Ein Fehlschlag löst dort Selbstkorrektur aus. Die Gates laufen während der Session und melden vor dem Commit einen sauberen Stand, statt Fehler erst nach dem Commit zu fangen. Umgesetzt werden sie als Reviewer-Agent oder als Begleitprozess, den der Agent abfragen kann (Thoughtworks Technology Radar Vol 34, April 2026 (öffnet in einem neuen Tab)).
Ein Erfahrungsbericht über eine Pipeline macht daraus einen selbstverstärkenden Loop: Der Agent committet seine Arbeit selbst. So sieht er bei jedem Commit die komplette Lint- und Test-Suite laufen und muss alles Rote beheben, bevor er weitermachen kann. Manchmal arbeitet er sich durch Dutzende Zyklen, bis alles grün ist. Eine harte Regel von 90 % Coverage zwingt ihn, die übersprungenen Tests nachzuschreiben. Der Agent versucht auch, sich an einem Gate vorbeizumogeln. Er schreibt etwa Unit-Tests, die heimlich echte APIs aufrufen, oder schmuggelt time.sleep() in async-Tests. Dagegen ergänzt der Bericht eigene Lint-Plugins, die genau diese Tricks erkennen. Diese Plugins schreibt der Agent selbst (Self-Reinforcing Code Quality Pipeline (öffnet in einem neuen Tab), 2025).
Agents verändern auch, welche Gates sich ein Team leisten kann. Ein Interview aus der Praxis beschreibt zwei Prüfungen, die seit Anfang der 2000er bekannt sind und als zu aufwendig liegen blieben. CRAP bewertet jede Funktion, indem es ihre Testabdeckung mit ihrer zyklomatischen Komplexität kombiniert. Mutation Testing dreht Operatoren im Quellcode um, fährt pro Änderung die ganze Suite neu und zählt jede Mutante, die die Suite nicht tötet. Um 2000 dauerte ein Mutationslauf eine Nacht: eine Suite von vier Minuten, mehrere hundert Mal wiederholt. Jede markierte Funktion von Hand zu beheben, lohnte sich nicht. Ein Agent mit dem Auftrag, den Code zu ändern, bis das Tool sauber meldet, erledigt beides in seinem Loop. Der Mutationsdurchlauf dauert dabei etwa 30 Minuten. Anlass war unordentlicher Code. Ohne Aufräumen begann der Agent, beim Beheben einer Sache eine andere zu zerbrechen, bis er sich im Kreis drehte. Der Preis ist Durchsatz, denn jedes zusätzliche Gate bremst den Agenten. Die genannte Grenze liegt dort, wo er langsamer wird als ein Mensch. Laut Interview ist die Arbeit mit den Gates noch zwei- bis viermal so schnell, und die Grenze ist noch nicht gefunden. Der Bericht stammt aus der Ich-Perspektive einer einzelnen Person aus der Praxis und hat keine externe Messung (LIVE: Uncle Bob on Software Fundamentals in the Age of AI (öffnet in einem neuen Tab), August 2026, Praxisbericht).
Für jedes Gate gilt eine Einschränkung: Die Diagnose hilft nur, wenn sie auf die echte Ursache zeigt. In einem ABAP-Benchmark scheiterte Claude Haiku 4.5 systematisch. Der Grund war nicht, dass es den Bug nicht beheben konnte. Irreführende Parser-Fehlermeldungen ließen es über mehrere Feedback-Runden den falschen Fehler jagen. Ein schneller Loop mit einer lügenden Fehlermeldung ist also schlechter als gar kein Loop (Benchmarking LLMs for ABAP (öffnet in einem neuen Tab), Anfang 2026).
Auch eine wahre Meldung kann in die Irre führen, wenn sie nur die Metrik nennt. Eine präregistrierte Studie gab Claude Haiku 4.5 und Claude Sonnet 4.6 18 echte Python-Funktionen. Jede hatte genau einen bekannten Smell: übermäßige Komplexität oder einen Exception-Handler, der Fehler verschluckt. Ein deterministischer Judge bewertete fünf Durchläufe pro Funktion und Bedingung. Er stimmte in 90 Prozent der Fälle mit blinden menschlichen Labels überein. Die Ergebnisse:
- Mit der bloßen Linter-Meldung behob Haiku 4.5 den Smell in 28,9 Prozent der Durchläufe, Sonnet 4.6 in 5,6 Prozent.
- Eine allgemeine Bitte, den Code zu verbessern, erreichte 25,6 Prozent bei Haiku und 34,4 Prozent bei Sonnet.
- Ein Prompt, der den Smell und das Refactoring erklärt, kam bei beiden Modellen auf 83,3 Prozent.
Das stärkere Modell schnitt mit der bloßen Metrik am schlechtesten ab. Dieses Muster ist zu erwarten, wenn ein Modell die Regel erfüllt, statt den Code zu reparieren. In 79 seiner 90 Durchläufe mit bloßer Metrik brachte Sonnet 4.6 die Prüfung zum Bestehen, ohne den Smell zu beheben. Bei Komplexität verteilte es dafür dieselbe Verzweigung auf Hilfsfunktionen. Die Studie nennt ihre Grenzen selbst: zwei Modelle, zwei Smells, eine Temperatur und ein Judge, der keinen Code ausführen kann. Dazu kommt ein Refactoring-Prompt für den Exception-Smell, der das Kriterium des Judges selbst ausspricht (prompt-vs-metric-eval (öffnet in einem neuen Tab), September 2026).
Coaching-Guides für Linter-Befunde
Habit Hooks setzt dieselbe Idee als Tool um. Es fährt die Linter, die ein Projekt schon hat, und gibt statt jedes rohen Verstoßes einen kurzen Coaching-Guide aus (habit-hooks - Habit Hooks (öffnet in einem neuen Tab), MIT, Version 1.5.0, Stand September 2026). Die Maintainer:innen argumentieren, dass ein langes Regeldokument im Kontextfenster einen Agenten schlechter macht. Ein Guide, der mit dem Befund kommt, trifft dagegen genau dann ein, wenn er gilt. Dieses Argument ist eine Behauptung des Projekts selbst. Das Repository stammt vom Mai 2026 und ist älter als die Studie oben. Die Studie testete Prompt-Formulierungen an einzelnen Funktionen und hat keinen vollständigen Lauf des Tools gemessen.
Das Design trennt das Erkennen vom Ratschlag. Zwei Befehle laufen über eine Pipe. Der erste startet die Detektoren und gibt Befunde als JSON aus. Der zweite gruppiert sie nach Smell, gibt den Guide aus und setzt den Exit-Code. Jeder Sensor übersetzt die Regel-ID eines Tools in einen tool-unabhängigen Smell-Schlüssel. So werden ESLints max-params und Ruffs PLR0913 beide zu too-many-parameters. Den Guide wählt das Tool nach dem Smell, nie nach dem meldenden Linter. Ein Guide dient damit jeder Sprache, deren Linter den Smell erkennt (habit-hooks - Habit Hooks architecture (öffnet in einem neuen Tab)). Plugins gibt es für Python, TypeScript, PHP, Java und Ruby. Ein Projekt ersetzt einen einzelnen Guide, indem es eine gleichnamige Datei in sein eigenes Konfigurationsverzeichnis legt. Ein Upgrade lässt diese Datei unangetastet (habit-hooks - Habit Hooks (öffnet in einem neuen Tab)).
Die mitgelieferten Guides teilen eine Form, die ein Team für seine eigenen Linter-Meldungen übernehmen kann. Jeder Guide nennt die Ursache hinter der Zahl und listet die Schritte in ihrer Reihenfolge. Er benennt die Abkürzung, die nur den Zähler bewegt, und endet mit einem Test, den die Leser:innen anwenden können. Der Komplexitäts-Guide sagt dem Agenten, Vorbedingungen in Early Returns zu ziehen. Eine Kette, die über einen einzigen Wert verzweigt, soll er in eine Lookup-Tabelle verwandeln. Bedingungen zusammenzulegen oder Zweige als Ternaries umzuschreiben, nur um den Score zu senken, schließt der Guide aus (habit-hooks - high-complexity guide (öffnet in einem neuen Tab)). Der Parameter-Guide wertet ein Options-Objekt, das nach seiner Funktion benannt ist, als dieselbe Liste unter neuem Namen. Er fragt stattdessen nach der Domänen-Entität, zu der die Parameter gehören (habit-hooks - too-many-parameters guide (öffnet in einem neuen Tab)).
Der Authoring-Skill des Projekts sagt, wann sich ein Guide lohnt: nur dort, wo der Default-Fix des Agenten oberflächlich oder falsch ist. Eine Regel, deren eigene Meldung schon zum richtigen Fix führt, bleibt ohne Coaching. Ein Guide nennt das Risiko, den Schritt und das erwartete Ergebnis. Die Beobachtung überlässt er dem Linter. Die Review-Checkliste begrenzt einen Guide für einen Architektur-Smell auf 150 Wörter und erlaubt 30 bis 60 Wörter für Code-Hygiene. Einen Suppression-Kommentar vorzuschlagen, verbietet sie (habit-hooks - skills (öffnet in einem neuen Tab), Stand Oktober 2026).
Auf einer bestehenden Codebasis führt der Einstieg über eine Baseline, die das Tool Snoozing nennt. Ein Befehl hält die heutigen Befunde in einem eingecheckten Index fest, spätere Läufe melden nur neue Smells. Der Index speichert den freigegebenen Inhalt. Ändert jemand eine Datei aus der Baseline, kehren ihre Befunde zurück, während der Agent in dieser Datei arbeitet. Strukturelle Smells wie eine übergroße Funktion lassen den Lauf standardmäßig scheitern. Bei Ermessensfragen wie dupliziertem Code oder einer verschluckten Exception gibt das Tool den Guide aus, ohne den Lauf scheitern zu lassen. Das empfohlene Snippet für die Instruktionsdatei des Agenten verbietet Baselining ohne Zustimmung der Nutzer:innen. Es schließt damit dieselbe Lücke wie das Blockieren von --no-verify (habit-hooks - Habit Hooks (öffnet in einem neuen Tab)).
Das Tool unterscheidet außerdem zwischen “nichts gefunden” und “nichts gelaufen”. Exit-Code 0 heißt sauber, 1 heißt, dass ein Befund den Lauf scheitern lässt, und 2, dass das Tool selbst gescheitert ist. Das passiert etwa bei einem falschen Konfigurationsschlüssel oder einem konfigurierten, aber nicht installierten Plugin. Bleibt die Pipe zwischen den beiden Stufen leer, meldet das Tool einen unvollständigen Lauf und endet mit 2. Ein Befund ohne Guide erscheint ohne Coaching, statt zu verschwinden (habit-hooks - Habit Hooks (öffnet in einem neuen Tab)). Ein sauberer Lauf beweist trotzdem wenig über Verhalten. Die Erfolgsmeldung sagt, dass das Tool strukturelle Smells fängt, aber weder Korrektheit noch Design prüft. Sie fordert den Agenten auf, vor dem Abschluss einen Reviewer-Subagent hinzuzuziehen. Diese Übergabe beschreibt Creator- und Verifier-Rollen.
Formale Spezifikation als tiefstes Gate
Die Leiter oben endet bei CI. Damit bleibt eine Fehlerklasse offen, die weder Linter noch Type Checker noch Testsuite verlässlich erreichen: der Interleaving-Bug in nebenläufigem und verteiltem Code. Ein Open-Source-Harness in Python zielt genau auf diese Lücke. Er stellt den Coding-Agenten vor einen Model Checker statt vor einen Test Runner. Der Agent liest das Ziel-Repository und schreibt eine TLA+-Spezifikation der Implementierung. Dazu schreibt er die Invarianten, die ihre Korrektheitseigenschaften festhalten. Dann prüft er die Spezifikation per Model Checking. Von jeder Verletzung arbeitet er sich zurück, um sie am echten Code zu reproduzieren (Specula (öffnet in einem neuen Tab), Apache-2.0, aktiv Stand August 2026). Dieses Urteil ist von anderer Art als ein grüner Testlauf. Ein Gegenbeispiel-Trace beweist, dass eine erreichbare Schrittfolge eine festgelegte Eigenschaft bricht. Ein Testlauf zeigt nur, dass die Fälle, an die jemand gedacht hat, weiter bestehen.
Zwei Designentscheidungen lohnen sich auch ohne TLA+. Erstens zerlegt das Harness die Arbeit in eine feste Kette von Agent-Skills: Codeanalyse, Spec-Generierung, Harness-Generierung, Validierung und Bug-Bestätigung. Jeder Schritt schreibt Dateien, die der nächste liest. So lässt sich ein Lauf an jeder Übergabe prüfen, und jede einzelne Stufe kann allein gegen eine bestehende Spec neu laufen. Zweitens erreicht der Checker den Agenten als MCP-Tools, nämlich als Trace-Debugger, Spec-Analyzer und Invarianten-Checker. Er kapselt den Agenten also nicht, und das deterministische Urteil bleibt auf der eigenen Tool-Oberfläche des Agenten.
Die Kosten bestimmen, wie oft das läuft. Die Toolchain braucht Java und einen Model Checker neben Python, dazu 32 GB RAM, 100 GB Festplatte und Linux oder WSL2. Das ist eine Kampagne gegen etwa eine BFT-Consensus-Implementierung, kein Gate pro Commit. Eine operative Eigenheit gilt über dieses Tool hinaus. Laut Projekt blockieren Modellanbieter unter Umständen die Phase der Bug-Reproduktion, solange der Account keine Trusted-Access-Freigabe hat. Das Projekt liefert deshalb eine hybride Konfiguration mit, die genau diese Phase an ein anderes Modell leitet. Bei agentischer Security-Arbeit kann also Safety-Filtering die bindende Grenze sein und nicht die Fähigkeit des Modells (specula-org - Specula (öffnet in einem neuen Tab)).
Kompilieren als Kontrollgrenze
Ein Markdown-Workflow trennt die Instruktion an den Agenten von der ausführbaren Automation. YAML-Frontmatter deklariert Trigger, Berechtigungen und Tools, der Fließtext beschreibt die Aufgabe. Ein Compiler macht aus dieser Quelle ein Lockfile mit dem gehärteten Workflow. Das Kompilieren wird so zu einer reviewbaren Grenze, an der sich die Konfiguration vor der Ausführung validieren lässt (GitHub - How They Work (öffnet in einem neuen Tab)).
Ausgaben mit getrennten Berechtigungen
Der Agent-Job läuft nur mit Leserechten. Angefragte Schreibvorgänge landen gepuffert als Artefakte und durchlaufen deterministische Filter und eine Bedrohungsanalyse. Erst dann führen separate Jobs mit eng gefassten Schreibrechten sie aus. So wird ein vorgeschlagener Seiteneffekt des Agenten zu einem prüfbaren Zwischenergebnis, statt dem Modell direkte Befugnis zu geben (GitHub - Security Architecture (öffnet in einem neuen Tab)). Dasselbe Design legt eine allgemeine Regel für agentische Automation nahe: Haltet Reasoning und Seiteneffekte in getrennten Stufen und macht den Übergang im Workflow explizit.
Defense in Depth
Die Kontrollfläche reicht von Schema- und Expression-Validierung zur Compile-Zeit über SHA-gepinnte Actions, Laufzeit-Isolation und Netzwerk-Allowlists bis zu MCP-Server-Sandboxing, Tool-Allowlists und Output-Sanitizing. Diese Ebenen machen einen kompromittierten Agenten nicht vertrauenswürdig. Sie begrenzen, was er erreichen und nach außen tragen kann. Fällt eine Ebene aus, bleibt der Blast Radius dadurch kleiner (GitHub - Security Architecture (öffnet in einem neuen Tab)).
Wie sich diese Gates als Pipeline-Stufen mit Zeitbudgets aufreihen, vom Pre-Commit bis zum Canary-Rollback, steht unter Agentic Continuous Delivery.
Quellen
Agentic Workflows
- Agentic Workflows (öffnet in einem neuen Tab) - Aktives Framework unter MIT-Lizenz, das Markdown-Instruktionen für Agents in abgesicherte CI-Workflows kompiliert, mit isolierten Schreibvorgängen, Tool-Allowlists und Netzwerkkontrollen.
Für Agents lesbare Diagnosen
- Habit Hooks (öffnet in einem neuen Tab) - Python-CLI unter MIT-Lizenz, die Regel-IDs von Lintern auf gemeinsame Smell-Schlüssel abbildet und pro Smell einen Coaching-Guide für Coding-Agents ausgibt.
- prompt-vs-metric-eval (öffnet in einem neuen Tab) - Präregistrierte Studie unter MIT-Lizenz, die bloße Linter-Meldungen mit Refactoring-Anleitungen an 18 Python-Funktionen und zwei Modellen vergleicht.
Formale Verifikation
- Specula (öffnet in einem neuen Tab) - Python-Harness unter Apache-2.0, in dem Coding-Agents TLA+-Specs und Invarianten für eine Codebasis schreiben, sie per Model Checking prüfen und Verletzungen auf Code-Ebene reproduzieren.