Was es braucht, damit ein Coding-Agent bestehenden Code nicht nur versteht, sondern seine Arbeit auch selbstständig und sicher verifiziert.

Agentic AI kann die Softwareentwicklung deutlich beschleunigen – zumindest so lange, bis das Prüfen des erzeugten Codes fast genauso viel Zeit kostet wie das Schreiben. Genau an diesem Punkt setzt ein KI-Harness an: Er gibt dem Agenten klare Regeln, automatisierte Tests und verlässliche Grenzen für seine Arbeit.

Martin hat einen solchen Harness für ein über Jahre gewachsenes Brownfield-Projekt entwickelt und kontinuierlich erweitert. In diesem Artikel nimmt er uns mit hinter die Kulissen seiner Arbeit: Er zeigt, wie der Harness Schritt für Schritt entstanden ist, welche Ansätze sich bewährt haben, wo er nachbessern musste und wie Tests, Quality-Gates, Verifier und ein isolierter Docker-Runner heute zusammenspielen. So entsteht ein praxisnaher Einblick in den Weg vom unterstützenden Coding-Agenten hin zu einem sicheren, zunehmend autonomen Entwicklungsprozess.

Ein KI-Harness im Brownfield-Projekt: Martins Learnings aus der Praxis

Wenn man Software mit agentischer KI entwickelt, ist ein verbreiteter Ansatz ein Harness, den man um den Agent legt. So lässt sich auch in langlaufenden Projekten gute Codequalität sicherstellen. Es gibt viele Artikel und Videos, die die Grundlagen eines Harness und seine Funktionsweise beschreiben. Aber was bedeutet es, einen solchen Harness in ein bestehendes Projekt mit über einem Jahrzehnt Code-Historie und gewachsenen Altlasten einzubauen? Ich habe es getan — und hier ist, was ich dabei gelernt habe.

Coding ohne Harness

Der einfachste Weg, KI beim Programmieren zu nutzen, ist ein Agent-Tool in der CLI, als Desktop-Anwendung oder IDE-integriert — etwa Codex oder in meinem Fall Claude Code und Claude Desktop. Das ist schon eine enorme Hilfe, um sich in einer komplexen Codebasis zurechtzufinden, Fixes für Bugs zu finden oder einfach schneller Code zu schreiben. Im interaktiven Prozess habe ich die Überlegungen und Lösungswege des Agents mit ihm diskutiert, den Plan angepasst, die Art der Umsetzung angepasst — und konnte so meine Produktivität steigern. Der größte Teil des Produktivitätsgewinns kam aber aus der schnelleren Suche in der Codebasis und der schnelleren Analyse. Das Schreiben von Code selbst wurde zum Lesen von Code. Denn ich musste jede Zeile des Agents lesen, um sicherzustellen, dass sie nicht zu einem Drift mit der bestehenden Codebasis führt — und das Prüfen dessen, was die KI getan hatte, wurde fast so zeitaufwändig wie das Schreiben des Codes selbst.

Ein Ziel definieren

Die Lösung schien einfach: dem Agent genug Informationen geben, damit er Code schreibt, der nicht im Detail geprüft werden muss. Entscheidend war dabei, nicht einfach in Vibe Coding zu verfallen. Der Code muss weiterhin den SOLID-Prinzipien und Clean Code folgen, damit er auch in einem langlaufenden Projekt wartbar bleibt. Ich wollte den Effizienzgewinn agentischen Programmierens, ohne jede Zeile lesen zu müssen — mir das Recht verdienen, den Blick von der einzelnen Codezeile zu lösen und auf die Weiterentwicklung des Projekts zu richten.

Der Harness als Ziel

Das Ziel ist ein Loop, in den ein freigegebenes Work Item hineingeht und ein fertiger Pull Request herauskommt — aber erst, nachdem der Agent gegen die eigenen Tests und die bestehenden und teilweise neu aufgebauten Gates bewiesen hat, dass er fertig ist.

Der Harness-Loop: vom Work Item über lokale Gates und Verifier bis zum Pull Request

Kein Vendor-Lock-In

Etwas, das ich bei dem Harness auf jeden Fall vermeiden wollte, ist ein Vendor-Lock-In. Sämtliche Skills, Agents und sonstigen Funktionen des Harness sollen mit dem von mir verwendeten Claude Code genauso funktionieren wie mit Codex oder anderen Agent-Tools. Daher sind die tatsächlichen Abläufe in Markdown-Files außerhalb von Claude beschrieben und Claude übernimmt von dort nur die Inhalte. Es gibt ein agents.md und claude.md, wobei die claude.md-Datei nur einen Verweis auf agents.md enthält. Aktuell ist zwar vieles noch auf Claude basierend, aber in Tests konnte ich bereits nachweisen, dass eine entsprechende Anpassung für andere LLMs einfach möglich ist.

Den Harness aufbauen

Es gibt bereits fertige KI-Harnesse, die als Open Source Projekte angeboten werden. SAW wäre hier ein Beispiel. Dieses und andere ähnliche Projekte dienten als Inspiration für meinen Harness. Ich wollte sie nicht einfach übernehmen, sondern den Harness Schritt für Schritt und passend zur bestehenden Codebasis aufbauen. Auch enthalten diese fertigen Konstrukte oft viel mehr, als ich im Alltag tatsächlich brauche. Dabei ist auch wichtig, dass ein laufendes Projekt nicht für Wochen stillstehen kann, während der Harness aufgebaut wird und reift. Es ging also darum, Schritt für Schritt sich dem Ziel des autonomen Agenten anzunähern und dabei bereits früh Vorteile aus der teilweisen Umsetzung zu erhalten.

Bausteine identifizieren

Zunächst ist es wichtig, zu definieren, welche Elemente eines Harness in einem Brownfieldprojekt am meisten unterstützen. Wichtig ist es, den bestehenden Code nicht zu zerstören. Üblicherweise hat ein bestehendes Projekt bereits Tests und Regeln. Manche dieser Regeln sind vielleicht bislang nicht maschinell überprüfbar. Manche Bereiche werden bislang vielleicht nur händisch getestet oder gar nicht. Um die Qualität sicherzustellen, muss der Agent jedoch automatisiert seine Arbeit verifizieren können.

  • Hohe Testabdeckung
  • Regeln festschreiben
  • Aktuelle Dokumentation
  • Strenge Quality Gates
  • Helfende Skills entwickeln
  • Dem Agent ermöglichen, seine Arbeit zu verifizieren
  • Kontinuierliche Weiterentwicklung

Die Bausteine

Tests, Tests und noch mehr Tests

Je mehr Dinge durch Tests abgedeckt sind, umso mehr kann eine KI auch selbstständig einen Punkt abschließen. Dazu liefern Tools wie SonarQube oder NDepend eine statische Codeanalyse, die einen Drift in technische Schulden vermeiden kann oder zumindest verringert. In meinem Projekt wurde bereits SonarCloud eingesetzt. Was mir aber fehlte, war eine Prüfung auf die Einhaltung von architektonischen Richtlinien und Namensmustern.

Solange ein menschlicher Entwickler diese Entscheidungen trifft, ist das kein großes Problem, aber für die automatische Abwicklung durch KI wollte ich mich nicht darauf verlassen, dass sie das schon richtig machen würde. Glücklicherweise gibt es mit ArchUnitNET ein Open Source Projekt, das genau diese Lücke füllen kann. Dabei hilft Claude, die bestehende Architektur zu analysieren und bereits Regeln dafür aufzustellen.

Der eigentliche Wert liegt in Regeln, die der Compiler gar nicht erzwingen kann. In .NET verhindert die Projektstruktur ohnehin, dass eine untere Schicht nach oben referenziert — spannend sind die Regeln innerhalb einer erlaubten Referenz. Im Brownfield-Projekt kommt eine zweite Schwierigkeit dazu: Eine neue Regel lässt sich selten sofort für den gesamten Bestand erzwingen, weil es bereits Verstöße gibt. Die Lösung ist eine eingefrorene Baseline — der bekannte Altbestand wird ausgenommen, aber jeder neue Verstoß scheitert sofort im Build. Ein zweiter Test wacht darüber, dass die Baseline nicht verrottet: Wird ein Altfall repariert, zwingt er dazu, den Eintrag zu entfernen, und die Regel zieht sich automatisch enger.

So verhindert der Harness neuen Schuldenzuwachs, während der Altbestand Stück für Stück abgebaut wird — ohne das laufende Projekt anzuhalten.

Diese Analyse kann auch helfen, “Fast-Regeln” zu erkennen. In meinem Fall gab es im Business-Layer nur eine Manager-Klasse, die nicht vom BaseManager abgeleitet hat. Nach genauerer Prüfung wurde klar, dass tatsächlich ein anderes Suffix für diese Klasse besser war, wodurch diese Regel entstand: Jede Klasse im Business-Layer, deren Name auf Manager endet, muss von BaseManager erben — sonst ist der *Manager-Suffix eine Rollenverwechslung und der Typ gehört unter einen anderen Namen (*Exporter, *Service).

Solche Regeln als Code sind das, was generische Linter nicht ausdrücken können — und genau das, was einem autonomen Agenten erlaubt, seine Struktur mechanisch zu verifizieren, statt sie richtig zu meinen.

Dazu kommen die Tests, die in den meisten Projekten bereits verbreitet sind. Unit- und Integrationstests, die die Businesslogik prüfen. Hier lohnt es sich, ein Augenmerk auf die Testabdeckung zu legen. Seltener sind bereits UI-Tests. In meinem Fall gab es im bestehenden Projekt zwar Tests der Weboberfläche, aber noch keine des MAUI-Projekts. Solange es hier keine Tests gibt, kann ein autonomer Agent jedoch keine Work Items in diesem Bereich abschließen. Es lohnt sich also, in die Erweiterung dieser Tests zu investieren.

Codingguidelines und Regeln

Aktive Projekte haben meistens bereits definierte Codingguidelines. Hier reicht es aber nicht, dass diese in einem separaten Dokument niedergeschrieben oder gar nur mündlich kommuniziert werden. Sie müssen mit Analyzern geprüft werden, soweit das möglich ist. Das erlaubt es, die Einhaltung der Guidelines zuverlässig zu prüfen. In meinem Fall wurde bereits StyleCop verwendet. Wer nach einer Sammlung von Analyzern sucht, wird auch mit Roslynator fündig. Je mehr Regeln bereits mit Analyzern geprüft werden können, umso weniger hängt die Codequalität an der Interpretation des KI-Agenten.

Guidelines, die nicht in solchen Regeln abzubilden sind, wie beispielsweise die SOLID-Prinzipien oder Clean Code, können in Markdown-Dateien in einer Dokumentationssammlung erfasst werden. Eine solche Entwicklerdokumentation kann KI und Menschen gleichermaßen dienen.

Dokumentation

Mir ist eine umfangreiche Dokumentation der Codebasis und der Funktionen wichtig. Nicht nur erleichtert das den Neueinstieg in ein Projekt, es erleichtert vielmehr auch dem KI-Agenten, sich in der Codebasis zurechtzufinden. Typisch für ein über Jahre gewachsenes Projekt, hatte auch mein Ausgangsprojekt zwar eine Dokumentation, die aber nicht an allen Stellen aktuell war und auch nicht den gesamten Umfang des Projekts umfasste.

Eine Erstfassung der Dokumentation habe ich mit Claude Fable 5 durchgeführt. Dabei habe ich den aktuellen Codebestand und die bestehende Dokumentation zusammengeführt und im Repository abgelegt. Ich habe mich für das beste Modell, das mir zur Verfügung stand, entschieden, damit die Dokumentation eine gute Ausgangsbasis hat.

Damit die Doku aktuell bleibt, reicht auch ein schwächeres Modell, wie in meinem Fall Claude Sonnet 5. In einem nächtlichen Task zieht sich der Agent die Änderungen seit der letzten Überprüfung und aktualisiert entsprechend die Dokumentation. So kann ein menschlicher Eingriff auf einmal monatlich beschränkt werden, um den aktuellen Stand zu prüfen.

Was der nächtliche Task konkret tut:

  • Diff-getrieben: Was hat sich seit dem letzten Lauf in Repo und Backlog geändert?
  • Ordnet die geänderten Pfade den betroffenen Handbuch-Kapiteln zu — nur verhaltensrelevante Änderungen, reine Refactorings brauchen keine Doku-Änderung
  • Aktualisiert die betroffenen Abschnitte aus dem echten Code, minimaler Diff, die Stimme des Kapitels bleibt erhalten
  • Kann er ein Kapitel nicht sicher aktualisieren, markiert er es stale — statt zu raten
  • Schreibt nie direkt auf develop, sondern immer über Branch + Pull Request; das Review bleibt das Gate
  • Läuft auf dem günstigen Modell; eine monatliche Konsolidierung auf dem starken Modell prüft das ganze Handbuch auf Drift

Ursprünglich habe ich das mit einer Claude Desktop Routine umgesetzt. Diese hatte allerdings das Problem, dass sie fast immer in einer Berechtigungsfrage geendet hat, die ich dann am Morgen abnicken musste, damit die Aufgabe abgeschlossen wird. Außerdem verlor die Routine sehr häufig die Verbindung zu Azure DevOps und musste neu gestartet werden. Die Lösung dafür war das System mit dem isolierten Runner, das ich weiter unten beschreibe.

Quality Gates

Der Agent kann nur dann seine eigene Arbeit verifizieren, wenn es auch gute Quality-Gates gibt, die mit Tests und statischer Codeanalyse das Ergebnis prüfen können. Dazu hatte mein Projekt, wie viele andere Projekte auch, eine Pipeline, die im Zuge eines Pull Requests ausgeführt wird. Das benötigt allerdings viel Zeit und belegt einen Pipeline-Runner. Gleichzeitig pollt der KI-Agent regelmäßig, ob der PR bereits fertig ist. Das ist zwar beides auf jeden Fall weiterhin notwendig und gut, aber ich wollte die meisten Probleme schon früher abfangen. Dafür gibt es ein Skript in dem Entwicklungs-Loop, das sämtliche lokal ausführbaren Tests auch ausführt. Dabei ist es wichtig, auch die Analyzer auszuführen. Der Agent sieht ansonsten diese Coderegeln nie.

Die gesamten Quality-Gates, die ein Agent also durchlaufen muss, damit ein Punkt bereit ist für einen Pull Request, sind:

  1. Statische Analyse (.NET) — Kompilieren mit den Analyzern (StyleCop + projekteigene Regeln); die Verstöße werden gezählt und sichtbar gemacht. Wegen des Brownfield-Altbestands scheitert das Gate nicht hart an bestehenden Warnungen — die verbindliche New-Code-Prüfung übernimmt SonarCloud im PR.
  2. Skript-Analyse — PSScriptAnalyzer prüft die Build- und Automatisierungs-Skripte selbst; der Harness kontrolliert auch sein eigenes Werkzeug.
  3. Frontend-Lint & Formatierung — ESLint und Prettier als harte Gates; ein Verstoß bricht den Lauf ab.
  4. Backend-Tests — sämtliche .NET-Unit- und Integrationstests, inklusive der ArchUnit-Architekturtests. Optional nur die vom Diff betroffenen Projekte — mit einer Schutzwarnung, wenn uncommittete Änderungen dadurch ungeprüft blieben.
  5. Frontend-Tests — die Angular-Testsuite, ebenfalls wahlweise auf die betroffenen Teile beschränkt.

Am Ende steht eine Zusammenfassung aller Schritte mit Pass/Fail — schlägt einer fehl, ist der Lauf rot und der Punkt nicht bereit für einen Pull Request. Nicht enthalten sind die Prüfungen, die erst im PR laufen: das SonarCloud-Quality-Gate und das CodeRabbit-Review.

Helfende Skills entwickeln

Im Zuge der Arbeit mit dem Harness gibt es Dinge, die man immer wieder tut und wo es hilfreich ist, eine passende Vorlage im Zuge eines Skills aufzubauen.

Der implement-story Skill entspricht dem Ablauf, der weiter oben bereits beschrieben wurde. Der define-story Skill hat die Aufgabe, eine Anforderung zu schreiben, die die folgenden Punkte — das spec-gate — erfüllt. Dieses wird durch einen Sub-Agent, den spec-assessor, geprüft, damit ein frischer Kontext ohne Vorüberlegungen sich die Story ansieht.

Woran das Spec-Gate misst — noch bevor eine Zeile Code entsteht:

  • Testbare Akzeptanzkriterien — ein beobachtbares given/when/then mit klarem Pass/Fail, kein “verbessere X”.
  • Betroffene Bereiche identifiziert — durch Lesen des Codes gefunden, nicht geraten.
  • Keine offene Produkt- oder Kundenentscheidung — kein “kommt drauf an”, kein TBD.
  • Automatisch verifizierbar — ein Test oder der Build kann “fertig” beweisen.
  • Keine externe Koordination — kein anderes Team, keine Drittanbieter-Änderung, kein Secret, keine koordinierte Migration.

Am Ende steht eines von drei Urteilen: spec-ok (die Anforderung darf in die unbeaufsichtigte Umsetzung), spec-gap (die Anforderung selbst ist lückenhaft — zurück an den Menschen) oder human-verify-only (implementierbar, aber nur durch menschliche Prüfung abnehmbar — also nicht für den autonomen Loop).

Ein generate-mobile-ui-test Skill hilft bei der KI-unterstützten Erstellung von Tests der MAUI-Oberfläche. Je nach Anwendungsfall kommen mit der Zeit weitere Skills dazu.

Dem Agent die Verifizierung ermöglichen

Ein wichtiger Schritt, damit der Prüfaufwand weniger wird, ist unabhängige Verifikation des Ergebnisses. Ein eigener Sub-Agent, der mit einem frischen Kontext startet, verifiziert das Resultat. Er erhält den Inhalt des Work-Items und die Änderungen am Code, jedoch nicht die Überlegungen des ausführenden Agenten, warum etwas gemacht wurde. Der Verifier prüft nun, ob der Sinn des Work-Items mit diesen Änderungen tatsächlich erfasst und umgesetzt wurde. Nur wenn dieser Verifier zufrieden ist, wird ein Pullrequest erstellt. Der Verifier sollte mit einem starken Modell ausgeführt werden. Ich habe mich hier für Claude Opus entschieden.

Woran der Verifier misst — alle Prüfungen in einem Durchgang, ein Urteil:

  • Test-Treue — kodiert jeder Test tatsächlich das beobachtbare Ergebnis seines Akzeptanzkriteriums, oder ist er eine Tautologie, die nur die Implementierung zurückspiegelt? (Der wertvollste Fang: ein grüner Test beweist nichts, wenn er das Falsche prüft.)
  • Intent vs. Gaming — erfüllt der Diff den Sinn des Work-Items, oder nur den Buchstaben der Tests?
  • Coverage — bleibt ein Akzeptanzkriterium von Code oder Test unberührt?
  • Security — führt der Diff eine Schwachstelle ein (Berechtigungslücke, Injection, Secret-Leak …)?
  • Spec-Treue — erfassen die Akzeptanzkriterien das Item überhaupt vollständig, oder legt erst das Bauen eine Lücke offen?

Am Ende steht genau eines von drei Urteilen: bestätigt (der Pull Request wird geöffnet), widerlegt (zurück in die Schleife, Code oder Test nachbessern) oder Spec-Lücke (die Anforderung selbst ist unvollständig — zurück an den Menschen).

Kontinuierliche Weiterentwicklung

Der Harness ist kein feststehendes Konstrukt, das man einmal baut und nie wieder anfasst. Natürlich ist der Wartungsaufwand mit der Zeit geringer, aber besonders am Anfang ist es hilfreich, sich möglichst viele Informationen aus dem Ablauf des Harness zu ziehen. Ich habe hierzu Claude ein Dashboard machen lassen, das die Ausführungen mitprotokolliert. Bei jeder Ausführung des Docker-Runners werden auch Telemetriedaten mitprotokolliert. Außerdem ist das komplette Transkript weiterhin auslesbar.

Dashboard

Hier zeigen sich schon zwei wertvolle Dinge. Der Handbook-Upkeep ist 37-mal an fehlenden Berechtigungen gescheitert. Man kann über das Transkript dann erkennen, dass er versucht hat, mit Bash-Befehlen zu arbeiten und daran gescheitert ist:

Genau darin liegt der Wert der Telemetrie: Der Handbook-Upkeep braucht offensichtlich zusätzliche Rechte oder eine klarere Anleitung — und ohne die mitlaufenden Metriken würde man diesen Bedarf schlicht nie sehen. Das Zweite ist der Tokenverbrauch. Damit kann man einen Überblick behalten, wie aufwändig die Prozesse sind. Hier hilft offensichtlich Caching sehr viel. Dafür war keine besondere Anweisung nötig, sondern das macht Claude von selbst.

Ausführung und Sicherheit

Berechtigungen

Ein großes Augenmerk liegt auf den Berechtigungen, die eine KI auf einem Computer hat. Während Programme wie Claude Desktop und Claude Code selbst bereits Berechtigungssysteme mitbringen, sind diese allein nicht für eine sichere Ausführung ausreichend. Jeder, der bereits mit Claude gearbeitet hat, wird wissen, dass es sehr häufig Berechtigungsfragen gibt. Man neigt dazu, diese Dinge dann mit immer erlauben aus der Welt zu schaffen, oder, wie ich, einfach im Auto-Modus zu arbeiten. Das funktioniert, solange man interaktiv mit der KI arbeitet und ihr ein wenig auf die Finger schauen kann, oder wenigstens zeitnah eingreifen. Wenn sie jedoch autonom arbeiten soll, um wirklich in den Genuss von dem möglichen Effizienzgewinn zu kommen, ist das nicht ausreichend.

Zum Zeitpunkt, während ich an diesen Zeilen schreibe, wurden zwei Fälle bekannt, in denen eine KI außerhalb ihres Zuständigkeitsbereichs wichtige Daten unwiederbringlich gelöscht hat. Diese Fälle bestätigen meine Anstrengung, die KI nicht vollautonom auf mein System zu lassen.

Was nicht funktioniert hat, waren routinespezifische Berechtigungen in Claude Desktop. Entweder galten die Erlaubnisse für das gesamte Projekt und jede Routine, oder sie wurden nur einmalig akzeptiert. Claude selbst konnte mir dabei keine Lösung anbieten. Da es aber noch das Problem des nächsten Absatzes gab, war das auch kein Problem, mit dem ich mich lange aufhielt.

Die Gefahr des angemeldeten Benutzers

Im Normalfall läuft ein KI-Agent in einer Session, die von einem angemeldeten Benutzer gestartet wurde. Als Entwickler sind wir dabei oft mit einer Vielzahl von Rechten ausgestattet. Wir haben möglicherweise Zugriff auf Netzwerkressourcen oder sind in diversen CLI Tools für Online-Systeme angemeldet. Betreibt man beispielsweise ein System auf Azure und ist über az login angemeldet, so hat der Agent alle Möglichkeiten, die man selbst auch hat. Und dazu zählt häufig auch, Ressourcen auf Azure zu löschen, Zugriff auf Produktivsysteme und andere schädliche Dinge, die nicht rückgängig zu machen sind. Auch mit reinem Git-Zugang könnte ein git push --force im schlimmsten Fall Inhalte unwiederbringlich vernichten. Je eigenständiger man einen Agenten handeln lässt, umso realer wird eine solche Gefahr. Zwar habe ich nie erlebt, dass eine KI ungefragt solche Dinge versucht hätte, aber wenn das Problem nur eine Prompt-Injection entfernt ist, ist das zu nah für autonome Ausführung.

Eingeschränkte Runner

Um sowohl aufgabenspezifische Berechtigungen als auch die Gefahr des angemeldeten Benutzers zu reduzieren, habe ich mich für einen Docker-Container Runner entschieden. Innerhalb des Docker Containers läuft Claude ohne direkten Zugriff auf das restliche System. Er arbeitet mit einem eigenständigen Checkout des Projekts. Für jede Aufgabe erhält er ein eigenes, im Repository eingechecktes Berechtigungsprofil (eine settings.json), das ihm genau die Rechte gibt, die er für die Erfüllung der Aufgabe braucht — versioniert und nachvollziehbar, statt über die Zeit interaktiv angesammelt zu werden. Da die meisten auf Azure DevOps zugreifen, erhält er auch einen Zugang per MCP über ein PAT-Token, das ausschließlich Work Items (Read & Write) und Code (Read & Write) darf.

Der eingeschränkte Runner: Host reicht Checkout, Profil und Auftrag in den isolierten Docker-Container, Claude spricht nur über einen eingeschränkten PAT mit Azure DevOps

Durch den Aufbau mit dem Hineinreichen von Rechten und Anweisungen kann derselbe Container und derselbe Aufbau für verschiedene Aufgaben verwendet werden. Sowohl für die nächtlichen Prozesse als auch für einen Implementierungsloop. Größere Teams könnten sich hier überlegen, einen eigenen Bot-User in den jeweiligen Systemen anzulegen, statt über PAT zu arbeiten.

Was ich gelernt habe

Interaktive Genehmigungen und MCP-Verbindungen skalieren nicht in den unbeaufsichtigten Betrieb. Die Claude-Desktop-Routine hing morgens an offenen Berechtigungsfragen und verlor ständig die Azure-DevOps-Verbindung. Der strukturelle Fix war nicht mehr erlauben, sondern der headless Runner: Eine nicht erlaubte Aktion wird sofort fail-fast abgelehnt und beendet den Lauf mit Bericht — statt über Nacht zu hängen. Die MCP-Server sind fest ins Container-Image gebacken.

Das lokale Gate muss das CI-Gate exakt spiegeln. Zumindest, soweit es möglich ist. In meinem Fall enthielt das Projekt SonarCloud, das erst innerhalb des PR-Gates läuft, doch Analyzer, Unit- und Integrationtests und ein unabhängiger Verifier helfen dabei, möglichst viele Themen bereits lokal abzudecken, ohne dass der zeitaufwändige Schritt über ein PR-Gate gemacht werden muss.

Ein Konventionsbruch ist ein Bug im Harness, nicht im Output. Liefert der Agent Code, der funktional richtig, aber strukturell falsch ist, patcht man nicht die eine Datei — man schärft die Regel (Analyzer, ArchUnit-Fitness-Function, Skill). Der Fix an einer Datei hilft einmal; die geschärfte Regel wirkt über das ganze Projekt.

Feedback durch die Agents selbst. Wenn Agenten auf Probleme stoßen, müssen sie das irgendwie mitteilen. Einfach ein Kommentar im Code hilft nicht, wenn nie ein Pull-Request daraus entsteht. Meine Lösung dafür war das Runner-Dashboard. Diese Daten werden auf dem ausführenden Computer gesammelt und können später ausgewertet werden.

Review-Bot-Kommentare (CodeRabbit) sind Vorschläge, keine Befehle. Sie sind regelmäßig schwach oder schlicht falsch. Jeder Kommentar wird auf seine Substanz geprüft: fixen, begründet ablehnen oder für später vormerken — nie stillschweigend übernehmen. Wiederkehrende valide Funde werden zur erzwungenen Regel (siehe Punkt davor), statt immer wieder von Hand gefixt zu werden. Seit Kurzem beantworte ich solche wiederkehrenden, aber unzutreffenden Vorschläge zusätzlich mit @coderabbitai. Damit übernimmt CodeRabbit diese Antworten als projektbezogene Hinweise und merkt sie sich für zukünftige Analysen.
Ein repräsentatives (nachgebautes, anonymisiertes) Beispiel für einen abgelehnten Vorschlag:
CodeRabbit: Consider wrapping this call in a try/catch to handle potential exceptions.
Antwort des Agenten: Fehler müssen hier bis zum globalen Handler durchschlagen (siehe Fehlerbehandlungs-Konvention); ein lokales try/catch würde genau die Ausnahmen verschlucken, auf die das Monitoring angewiesen ist. Bleibt wie es ist.

Mehr Dokumentation ist nicht besser. Wenige veraltete oder duplizierte Stellen zerstören das Vertrauen in die ganze Doku — und fressen Kontext, den der Agent besser für die Aufgabe nutzt. Regel: skimmbar, keine Duplikation dessen, was Analyzer und ADRs ohnehin besitzen; wächst ein Kapitel über die Lesbarkeit hinaus, wird es geteilt, nicht länger geschrieben.

Der Agent braucht Anweisungen für die Dokumentation. Ein wiederkehrendes Problem war, dass mit der Zeit die Inline-Dokumentation im Code falsch wurde. Teilweise sogar innerhalb desselben Features. Das hing damit zusammen, dass in einem Kommentar beispielsweise beschrieben wird, von wem die Methode aufgerufen wird. Wird die aufrufende Methode jetzt umgebaut, achtet der Agent nicht darauf, dass damit der Kommentar in einer ganz anderen Datei falsch wird. Hier lohnen sich explizite Vorgaben, die den Umfang der Dokumentation einschränken und auch vorgeben, dass der Agent keine Kommentare darüber abgeben soll, wie andere Dateien damit umgehen. Für einen Entwickler mag das klar sein, doch der KI-Agent neigt dazu, zu viel zu dokumentieren und damit unweigerlichen Drift zu erzeugen.

Was heute ist und morgen wird

Was heute ist

Mit dem heutigen Stand hilft der Harness bereits deutlich bei der täglichen Arbeit. Die Definitionen und Guidelines helfen auch bei interaktiven Sessions, wenn nicht über den implement-story Skill gearbeitet wird. Die verbesserte Sicherheit bei den Berechtigungen erhöht das Vertrauen beim Arbeiten mit dem Auto-Modus. Die zusätzlichen Tests geben nicht nur der Arbeit mit KI zusätzliche Sicherheit, sondern auch menschlicher Entwicklung. Und drei Automatismen laufen bereits regelmäßig, ohne dass ich eingreifen muss:

  • Dokumentation: Jede Nacht werden durch den nightly-handbook-upkeep-Task die Änderungen in das Dev-Handbook übernommen.
  • Log-Aggregator: Jede Nacht sucht nightly-sentry-triage nach dem schlimmsten, noch nicht aufgenommenen Bug in den Logs und legt dafür einen Punkt an.
  • Dashboard: Jeden Morgen wird das lokale Dashboard aktualisiert, um die Ausführungswerte zu erhalten.

Was morgen wird

Im Moment wird der implement-story-Skill noch per Hand angestoßen. Auch dieser soll in den Docker-Runner wandern und so mit eingeschränkten Rechten arbeiten. Ein weiterer Punkt, mit dem man den Harness erweitern kann, ist das Auslagern in ein gesondertes Repository, damit es in anderen Projekten wiederverwendet werden kann. Dass das funktioniert, konnte ich in meiner privaten Umgebung bereits nachweisen.

Insgesamt finde ich die Arbeit mit einem KI-Agent innerhalb eines Harness angenehmer als ohne ein solches Sicherheitsnetz. Bereits während der Umsetzung konnte ich die Früchte meiner Arbeit genießen und zunehmend mehr der KI anvertrauen, ohne die Qualität zu verlieren. Nun muss sich der Harness im produktiven Betrieb weiter beweisen und zeigen, dass ich meinen Blick von der einzelnen Codezeile lösen und mehr auf die Weiterentwicklung und Innovation des Projekts richten kann.

Der Harness ist damit noch lange nicht am Ziel – aber der Weg dorthin wird zunehmend greifbar. Wie er sich im produktiven Betrieb bewährt, welche neuen Herausforderungen dabei entstehen und was auf dem Weg zum autonomen Implementierungs-Loop noch dazukommt, folgt in einem späteren Artikel.

Der Artikel in a nutshell - für alle die es eilig haben 😉

Ein KI-Harness macht einen Coding-Agenten nicht automatisch autonom. Er schafft die Voraussetzungen dafür, dass ein Agent Aufgaben zuverlässig umsetzen, seine Arbeit selbstständig prüfen und dabei innerhalb klar definierter Grenzen handeln kann.

Dafür braucht es präzise Work Items, automatisierbare Akzeptanzkriterien, umfassende Tests und lokale Quality-Gates, die die CI-Pipeline möglichst genau abbilden. Ein unabhängiger Verifier prüft zusätzlich, ob nicht nur die Tests grün sind, sondern auch der eigentliche Intent der Anforderung erfüllt wurde.

Damit autonome Ausführung sicher bleibt, arbeitet der Agent in einem isolierten Docker-Runner mit minimalen Berechtigungen. Gleichzeitig wird der Harness kontinuierlich weiterentwickelt: Wiederkehrende Fehler werden in neue Regeln, Tests oder Skills übersetzt und machen das System Schritt für Schritt verlässlicher.