Startseite / Artikel

Ein Produktions-Harness für Claude Code: Sechs Schichten von Slack bis zur zusammengeführten MR.

Wie ein sechsschichtiger Harness Slack-Tickets in zusammengeführten Code umwandelt: Eingang von Ereignissen, geschützte Agentenschleifen, dauerhafter Zustand, Selbstheilung, Überwachbarkeit und Steuerung durch den Operator.

Verlag: REACTAPP.TOP42 Min. Lesezeit9771 Wörter

Quellen: Building a Production Agent Harness: Turning Claude Code Into a Multi-Agent Engineering Pipeline

Setzt man ein KI-Modell allein ein, erhält man im Grunde einen „Kopf in einem Glas“. Es kann Code schreiben und Funktionen aufrufen – doch es wird weder Ihre privaten Slack-Nachrichten um 3 Uhr morgens lesen, noch einen fehlgeschlagenen CI-Job erneut ausführen, noch Konflikte in offenen Merge Requests lösen oder daran erinnern, dass ein Reviewer gestern eine Frage gestellt hat und niemand geantwortet hat.

Was das Modell zu etwas Nützlichem macht, ist das zugrundeliegende Framework: die Laufzeitarchitektur, die „Sinne“, „Hände“ und „Gedächtnis“ bereitstellt. Zu ihren Bestandteilen gehören die Aufnahme von Ereignissen, die Orchestrierung von Agenten, dauerhafte Zustandsverwaltung, selbstheilende Schleifen, Überwachungsfunktionen sowie Möglichkeiten für den menschlichen Operator, das System zu steuern. Das unten beschriebene System ist ein Produktivsystem – keine Forschungsdemo. Es überwacht einen Slack-Kanal, erstellt Merge Requests für fünf interne Repositorien, beantwortet über Nacht die Kommentare der Reviewer und repariert fehlgeschlagene CI-Ausführungen ohne Aufforderung.

Es handelt sich um einen internen, mehragentigen Pipeline-System, das auf Claude Code basiert. Es läuft bereits seit mehreren Monaten ununterbrochen, übernimmt Aufgaben von Slack und leitet echte Ingenieurarbeiten weiter. Im Folgenden wird ein Überblick gegeben: Was es tut, wie die einzelnen Komponenten miteinander verbunden sind sowie die Entwicklungsprobleme, die das Design geprägt haben.

Die Abschnitte werden in dieser Reihenfolge behandelt: Warum überhaupt ein solches System nötig ist, was dieses System leistet, die drei Lücken, die es schließt, sechs Ebenen (Ereignisverarbeitung, Agent-Orchestrierung mit näherer Betrachtung des autonomen Schleifenmechanismus und der Selbstverbesserungs-Ebene, persistenter Zustand, Selbstheilungsfunktionen, Überwachbarkeit sowie menschliche Steuerung), einige zusätzliche Aspekte außerhalb der Ebenen, eine Schritt-für-Schritt-Anleitung sowie ein Vergleich mit anderen ähnlichen Systemen.

Warum eine Chat-Window und einige Tools nicht ausreichen

Als im Jahr 2023 Plugins für Chat-Assistenten auftauchten, versuchten viele Teams den offensichtlichen Kurzweg. Sie setzten eine Chat-Oberfläche vor ein LLM, gaben ihm einige Tools zur Ausführung von Funktionen und bezeichneten das Ergebnis als „den Engineering-Agenten“. Diese Konfiguration funktioniert fast in einer Demo, überlebt aber nie in der Produktion. Bereits innerhalb einer Woche treten drei Fehlermuster auf.

  1. Der Kontext verschwindet, wenn der Benutzer das Tab schließt. Das Gedächtnis muss länger bestehen als eine einzelne Chat-Sitzung. Eine echte Ingenieurstaufgabe dauert Tage: Ticket erstellen, neue Branch anlegen, Entwurf verfassen, Überprüfung abwarten, Kommentare beantworten, auf grünes CI warten und schließlich fusionieren. Kein einzelner Modellaufruf kann all das speichern.
  2. Nichts reagiert auf äußere Veränderungen. Ein Überprüfer kommentiert um 16 Uhr. Das CI bricht beim dritten Commit zusammen. Ein Teamkollege antwortet im Thread. Der Agent muss auf diese Ereignisse reagieren, anstatt endlos zu prüfen oder darauf zu warten, dass jemand ihn erneut anweist.
  • Fehler können niemals behoben werden. Der Agent versucht weiterzumachen, die CI bricht zusammen, und der Operator muss am nächsten Tag zurückkehren und den Fehler von vorne erklären. Oder die Sitzung des Agents beim Identitätsanbieter läuft mitten in einer Aufgabe ab, wodurch die gesamte Pipeline ohne jegliches Signal zum Stillstand kommt. Beides kommt täglich vor, und Ingenieure werden abends und an Wochenenden zur Bearbeitung von Aufgaben gerufen, die eigentlich vom Agenten erledigt werden sollten.
  • Die Behebung dieser Probleme ist die Aufgabe des Harness. Genau das unterscheidet auch eine einfache Chat-Tab-Option von Claude Code selbst, von Cursors Hintergrundagenten, von Devin oder vom hier beschriebenen System. Das zugrunde liegende Modell ist bei allen nahezu austauschbar. Was sich unterscheidet, ist der Harness, der um dieses Modell herumgebaut ist.

    Das Modell erreicht die Außenwelt nur über den Harness, und der Harness übernimmt fast die gesamte Ingenieurarbeit, die dafür sorgt, dass ein LLM sich wie ein System verhält.

    Die Aufgabe, für die das Tool entwickelt wurde

    Das ursprüngliche Problem war konkret. Ein Slack-Kanal, nennen wir ihn #support, sammelt etwa 30 Tickets pro Woche. Jedes Ticket erfordert eine Untersuchung und in der Regel anschließende Codeänderungen in einem von fünf Repositories. Ein Ticket kann sich über mehrere Tage hinziehen. Reviewer kommentieren Pull Requests, CI-Tests scheitern manchmal, und einige Untersuchungen erweisen sich als Wiederholungen von denen des Vormonats. Das Team, das diese Last tragen musste, wurde ständig abends und an Wochenenden angerufen, und es verbesserte sich nichts, da immer wieder die gleichen Ticketmuster auftauchten.

    Das Tool hat drei Aufgabenbereiche, geordnet nach dem Grad der Einmischung:

    1. Untersuchen. Wenn ein neues Ticket in #support ankommt, verfolgt es den Thread, sammelt Kontext aus verschiedenen Repositories und veröffentlicht eine strukturierte Analyse in diesemselben Thread.
  • Behebung. Sobald der Operator zustimmt, wird eine Merge-Request mit der vorgeschlagenen Änderung erstellt, die Kommentare der Prüfer beantwortet, das CI überwacht, bei Fehlern repariert und die MR zur Fusion weitergeleitet.
  • Selbstverbesserung. Jeder abgeschlossene Fall liefert Rückmeldungen. Wenn sich ein Muster wiederholt – beispielsweise fünf Tickets in einem Bereich, die alle dieselbe Konfiguration betreffen – erfasst das System die Lücken und schlägt Änderungen an sich selbst vor.
  • Diese dritte Aufgabe unterscheidet dieses System von einfachen Ticketing-Bots. Die Rohstoffe des Systems sind sein eigener Quellcode, und das LLM kann durch Pull-Requests, die einer menschlichen Prüfung unterliegen, darauf schreiben.

    Das System besteht aus sechs logischen Schichten, von denen jede unten in eigenen Abschnitten behandelt wird.

    Die drei Lücken, die ein reines Modell nicht schließen kann

    Hinter der gesamten Konstruktion steht eine Idee: Ein zustandsloser, rein reaktiver LLM ist kein Produktivsystem. Die sechs Schichten dienen dazu, drei Lücken zu schließen, die das Modell allein nicht überbrücken kann.

    Die erste Lücke ist die Reaktivität. Ein Modell kann sich nicht selbst aktivieren. Schicht 1 (Ereignisverarbeitung) kümmert sich darum, da Slack-Meldungen, GitLab CI-Ergebnisse und PagerDuty-Seiten alle in eine einzige Dispatch-Queue fließen. Das LLM prüft niemals selbst nach neuen Daten – es wird einfach aufgerufen.

    Persistenz ist die zweite Lücke. Modelle vergessen alles zwischen den Sitzungen und verlieren den Kontext, wenn die Arbeit zwischen Maschinen verschoben wird. Zudem können sie nicht unterscheiden, ob etwas bereits bearbeitet wurde oder ob eine Änderung tatsächlich im Quellsystem angekommen ist. Schicht 3 (persistenter Zustand) behebt dieses Problem: In-Memory-Strukturen entfernen Duplikate innerhalb eines Prozesses, lokale JSON-Dateien speichern die operativen Informationen, und durch git-synchronisierte Arbeitsräume wird ein dauerhafter Zustand der Fälle gewährleistet. Die Sitzungskontinuität zwischen Maschinen, die später beschrieben wird, basiert auf derselben Idee – nämlich dass der Zustand im git-System liegen sollte und niemals innerhalb eines laufenden Prozesses.

    Die dritte Lücke ist die Qualität. Wenn sie unstrukturiert bleibt, zieht ein Reasoning-Loop eines LLM zuversichtliche Schlussfolgerungen aus dürftigen Beweisen, beendet Untersuchungen, bevor alle erreichbaren Quellen überprüft wurden, und erstellt MR-Beschreibungen, die von Anfang an wie maschinengeschrieben wirken. Layer 2 (Agent-Orchestrierung) sowie der Untersuchungsloop begegnen diesem Problem durch strukturierte Ausgabeverträge, eine Qualitätskontrolle bei jeder Iteration, eine gegenseitige Überprüfung nach Abschluss des Loops sowie selbstheilende Schleifen innerhalb des externen Zyklus aus Codeänderungen, CI-Tests und Merges.

    Die Kumulation ist es, die die Schichten miteinander verbindet, denn jeder abgeschlossene Fall beschleunigt den nächsten. Genehmigte Untersuchungen werden zu Einträgen in der Wissensdatenbank. Genehmigte MRs verwandeln sich in Muster, auf die der Lückenanalyse-Tool verweisen kann. Ein abgeschlossener Fall führt zu einem Verbesserungs-MR, das sich auf den Harness selbst richtet. Mit jedem Zyklus werden weniger Ticketarten schlecht bearbeitet.

    Ebene 1: Wie Ereignisse das System aktivieren

    Drei externe Signalquellen können das Harness aktivieren:

    • Slack-Nachrichten, also Kanalverweise, Antworten in Threads sowie Admin-Befehle, die als Privatnachricht an sich selbst gesendet werden.
    • Aktivitäten in GitLab, d.h. Kommentare zu Pull-Request-Überprüfungen, Ergebnisse von Pipelines sowie Pipeline-Fehler.
    • PagerDuty-Alarme, die verwendet werden, wenn eine On-Call-Seite auf ein Ticket verweist, das das Harness bereits gesehen hat, damit der frühere Kontext wieder sichtbar wird.

    Die drei Quellen unterscheiden sich in Latenz und Zuverlässigkeit, weshalb jede ihre eigene Eingangsroute hat.

    Zwei praktische Details kommen in der standardmäßigen Version dieses Diagramms nie vor.

    Eventuelle Konsistenz. Der Endpoint conversations.history von Slack kann um 1 bis 30 Sekunden im Rückstand sein. Ein Abfrageskript, das ein leeres Ergebnis sieht und naiv seinen Cursor voranschreibt, überspringt dauerhaft alle Nachrichten, deren ts-Wert in diesem Zeitraum liegt. Dadurch bleibt der Cursor bei leeren Abfragen „eingefroren“ und der nächste Versuch beginnt wieder mit dem gleichen ältesten Zeitstempel, bis zumindest eine Nachricht zurückkommt. Ein nach Nachrichten-ts sortiertes Deduplikationsset filtert die durch diese Wiederholungsversuche entstehenden Duplikate heraus.

    Socket Mode zusammen mit einem Poller – das perfekte Duo. Socket Mode liefert Antworten der Threads mit einer Latenz von unter einer Sekunde, doch WebSocket-Verbindungen können abbrechen. Der Poller fungiert als Sicherheitsnetz und fängt alles auf, was Socket Mode übersehen hat. Beide Methoden teilen sich eine socket-dedup-Datei, damit keine Nachricht zweimal versandt wird. Entfernt man diesen Schutz, verschwinden alle während eines abgebrochenen Verbindungszeitraums eingehenden Nachrichten stillschweigend, ohne dass der Betreiber erfährt, dass sie übersehen wurden.

    Der MR-Monitor-Cron-Loop verhält sich anders. GitLab schickt keine Überprüfungskommentare in den Pipeline, wodurch das Abfragen die einzige Option ist – und das Skalieren des Abfragens selbst erwies sich als schwierig. Jeder zusätzliche, lange laufende MR, den das System übernimmt, verlängert den 10-minütigen Loop, wodurch sich für tote Threads Cursor anhäufen, und jeder davon kostet pro Zyklus einen Slack-API-Aufruf. Ohne Eingreifen führten sie zu anhaltenden 429-Rate-Limit-Problemen, die das wichtige Abfragen behinderten. Die Lösung löscht einen Cursor automatisch, wenn die übergeordnete API thread_not_found antwortet, sodass er aus dem Loop des nächsten Zyklus entfernt wird. Vor der Lösung führte ein Zyklus stundenlang 12 Wiederholungsversuche durch; danach keine mehr.

    Layer 2: Wie Agenten orchestriert werden

    Der LLM läuft tatsächlich hier. Das Harness startet Arbeitsprozesse als vorübergehende systemd-Einheiten, und jeder davon erhält eine JSON-Datei mit Angaben zum zu ausführenden Workflow, dem zu verwendenden case_dir sowie der ihm zugeordneten Thread-Nummer.

    Jeder Pipeline entspricht einem einzigen langen Claude Code-Ausführungsvorgang (dem „Gehirn“), der eine sorgfältig ausgewählte Anfrage sowie einen schreibbaren case_dir erhält. Dieser Ordner dient als Arbeitsfläche des Agenten und enthält das TASK.md des Falls, eine wachsende Anzahl von Transkripten in der Form followup_transcript_*.md, alle von den Agenten erstellten gap_report*.md-Berichte sowie Zwischenergebnisse im JSON-Format.

    Die Reihenfolge, in der die Agenten arbeiten, ist Teil des Designs. Anstelle eines einzigen Agenten, der alles erledigt, sind mehrere spezialisierte Agenten miteinander verbunden, wobei jeder an einer festgelegten Stelle die Aufgabe weitergibt:

    • oncall_run führt die erste Untersuchung durch – bis zu 20 Überlegungsschleifen mit einer Qualitätsprüfung – und endet mit einer ausdrücklichen Selbstkritik. Sein Ausgabeformat besteht aus einer ANALYSIS.md-Datei, die beschreibt, was falsch ist, sowie einer TASK.md-Datei, die angibt, was zu tun ist.
    • case_followup ist der langzeitige Chat-Agent. Ein Operator kann tagelang per privater Nachricht in einem Fallthread antworten, und jede Antwort ruft case_followup erneut mit dem gesamten bisherigen Gespräch auf. Zudem ruft er alle N Antworten einmal gap_analyzer als Unteragent auf; dieser Kreislauf erzeugt die gap_report*.md-Dateien, die auf den eigenen Code des Systems verweisen (der Selbstverbesserungskreislauf wird weiter unten erläutert).
  • finalize_case ist der Konsolidator. Sobald ein Fall vier Stunden lang inaktiv war oder ein Operator admin: finalize <case> ausführt, sammelt es alle Lückenberichte ein, entfernt die Falschpositiven und eröffnet einen einzigen MR mit den überlebenden Korrekturen. Dieser MR bezieht sich auf die Harness-Codebasis und nicht auf das ursprüngliche Repo des Tickets – das ist der Selbstverbesserungskreislauf in konkreter Form.
  • dev-agent ist das Element, das die praktische Arbeit erledigt. In seinem eigenen git-Worktree pro Fall bearbeitet es den Code, führt Push-Aktionen durch, überwacht den CI-Prozess, behebt Störungen im CI und antwortet auf Kommentare der Prüfer. Es arbeitet in drei Modi: address_review, fix_ci und task (die ursprüngliche Implementierung).
  • In diesem Diagramm gibt es zwei Details, die leicht übersehen werden können.

    Zunächst berührt nichts den Haupt-Checkout, da alle Arbeiten in Worktrees durchgeführt werden. Dadurch können mehrere Fälle gleichzeitig ablaufen, ohne miteinander in Konflikt zu geraten. Der Poller sowie der Dev-Agent-Dispatcher fügen den Pfad des Worktrees zum Payload hinzu, sodass die LLM den Haupt-Checkout überhaupt nicht zu Gesicht bekommt.

    Zweitens dient case_dir als Ankerpunkt für alles. Zustandsdateien in ~/.harness/ (siehe Abschnitt zur Persistenz) verknüpfen jeden Slack-Thread mit einem case_dir, und der Agent interpretiert alle Pfade relativ dazu. Dadurch wird alles Weitere einfacher: Man verschiebt den Ordner, aktualisiert die Karte, und jeder Workflow folgt automatisch.

    Das Diagramm zeigt an, was ausgeführt wird. Der nächste Abschnitt erklärt, wie dies funktioniert – also die Mechanismen, durch die ein Slack-Befehl zu einer abgeschlossenen Untersuchung führt.

    Der autonome Kreislauf, Schritt für Schritt

    Falls jemand fragt, was dieses System eigentlich tut, lautet die kürzeste ehrliche Antwort: Ein Operator gibt einen Slack-Befehl ein, und anschließend läuft ein geschlossener Kreislauf aus wiederholtem Denken, Selbstkritik und Werkzeugnutzung unbeaufsichtigt weiter, bis die Arbeit abgeschlossen ist oder das System zugibt, nicht weitermachen zu können. Fast jede interessante Gestaltungsentscheidung verbirgt sich in diesem Satz, und der Rest dieses Abschnitts erläutert sie genauer.

    In dem System befinden sich vier unterschiedliche Agentenkreisläufe, wobei jeder für eine bestimmte Art von Problem konzipiert ist:

    Die Abbildung zeigt die Strukturen. Danach folgt der Inhalt, wobei jeder Mechanismus in der Reihenfolge seines Ablaufs beschrieben wird. Das Diagramm bezieht sich auf alle diese Mechanismen, und seine Knoten werden nach dem Lesen der folgenden Abschnitte verständlich. Die von ihm verwendeten Torcodes sind G1 bis G4, N1 bis N3, N5, A1 bis A3 sowie D1 bis D15.

    Vor dem Kreislauf: Der Vorab-Verpflichtungs-Sperre

    Zwei Schutzmaßnahmen werden vor dem ersten Claude-Aufruf getroffen, wobei die erste struktureller Natur ist. Bevor eine Untersuchungsrunde stattfindet, muss Claude im on-call-Pipeline zu einer hypothesis_slate übergehen. In dieser Liste sind mindestens sechs mögliche Erklärungen sowie vier oder fünf Kriterien zur Auswahl daraus aufgeführt, und sie wird in kickoff_precommitment.json gespeichert, wodurch danach nichts mehr geändert werden darf.

    Von allem im System ist dieses Schloss der Schutzmechanismus mit dem größten methodischen Gewicht und der geringsten Sichtbarkeit. Ein unbeschränkter Schlussfolgerungszyklus von KI-Modellen neigt dazu, sich für das zu entscheiden, was durch den ersten Beweis unterstützt wird, und alle späteren Beweise als Bestätigung zu werten. Das Schloss kann diesen Trend nicht aufhalten. Stattdessen zwingt es jede Untersuchung dazu, von dem gesamten Hypothesenraum aus zu beginnen und aufzuzeichnen, wie sie von dort zu ihrem Schluss gelangt ist. Der gegnerische Prüfer (Phase B) stellt anschließend eine schwierigere Frage als die nach interner Konsistenz: Ist dies der Schluss, den das ursprüngliche Modell angesichts der Beweise vorhergesagt hätte?

    Der zweite Schutzmechanismus ist die empirische Grundlage (Phase C). Erst in der ersten Iteration werden einige Befehle auf Betriebssystemebene ohne Einbeziehung eines LLM ausgeführt: Fehlerauszüge von journalctl, Snapshots von Festplatte und Speicher sowie eine Suche nach Log-Dateien. Die Ergebnisse werden als Rohdaten in die anfängliche Untersuchungsanfrage aufgenommen, wodurch Claude ein faktisches Bild der Maschine erhält, noch bevor irgendwelche Hypothesen bestehen. Dieser Schritt blockiert nie, ignoriert Fehler stillschweigend und dauert weniger als eine Sekunde. Er ist notwendig, weil der nächste Überlegungsschritt anschließend von den tatsächlichen Gegebenheiten ausgeht und nicht nur von der Problembeschreibung.

    Gestrahlte Ausgabe: das Abschlussbericht

    Jede Iteration endet mit einem parsierbaren JSON-Dokument, dem completion_report, und das Steuerungsprogramm liest dieses, um zu entscheiden, was als Nächstes geschieht. Seine Struktur ist festgelegt:

    {
      "status": "IN_PROGRESS" | "BLOCKED" | "COMPLETE",
      "confidence": 78,
      "open_questions": ["..."],
      "unchecked_sources": [{"name": "...", "access_status": "..."}],
      "contradiction_register": ["..."],
      "assumption_register": ["..."],
      "adjacent_problems": [{"summary": "...", "status": "...", "blocker": "..."}],
      "draft_response": "the user-facing Slack message"
    }
    

    Freier Prosa-Text kann den Block umgeben, doch die Prüfmechanismen bewerten den analysierten Bericht und die Zustandsdateien speichern ihn. Der größte Hebel, um die Ausgaben von LLMs innerhalb eines Pipelines nutzbar zu halten, bestand darin, etwas Freiheit in der Prosa gegen einen analysierbaren Vertrag einzutauschen. Wird das Schema weggelassen, muss jeder folgende Schritt die Fakten aus dem Prosa-Text wieder herausfiltern. Behält man das Schema bei, werden die Prüfmechanismen zu gewöhnlichem, deterministischem Python.

    Qualitätsprüfmechanismen, die Vertrauen eine Bedeutung geben

    Ein Vertrauenswert wäre reiner Marketing-Lärm, wenn Claude einfach confidence: 95 eingeben und fortfahren könnte. Deshalb durchlaufen die Ausgaben jeder Iteration Qualitätsprüfmechanismen, die es nicht zulassen, dass die Untersuchung weitergeht, sofern die Beweise und der angegebene Vertrauenswert nicht übereinstimmen.

    Nach jeder Aktion von Claude laufen drei Arten von Prüfmechanismen:

    • G-Türen überprüfen die logische Konsistenz, damit das Denken zwischen den Runden nicht mit sich selbst im Widerspruch steht. Der klarste Fall ist G1, der ausgelöst wird, wenn open_questions während der Runde zugenommen hat, während confidence gestiegen ist – denn ein zunehmender Nenner hebelt jede Aufwärtsbewegung wieder aus. Die Familie prüft außerdem, ob angespannte Formulierungen im Text als Widerspruch erkannt werden und ob benachbarte Probleme als gültige Statusobjekte statt als Platzhalterzeichen erfasst werden. Es gibt vier solcher Türen.
    • N-Türen überprüfen die strukturelle Vollständigkeit, indem sie sicherstellen, dass Artefakte vollständig ausgebildet sind und keine unvollendeten Strukturen darstellen. N1 verlangt, dass jede ausführbare Korrekturmaßnahme mit einer verify_action kombiniert wird, die eine Beschreibung, ein erwartetes Ergebnis sowie einen Zeitrahmen liefert. Eine vorgeschlagene Korrektur ohne Möglichkeit zur Überprüfung gilt als unvollendete Arbeit. Es gibt drei solcher Türen.
  • A-Tore setzen mechanische Obergrenzen, die der Prosa nicht umgangen werden können. A1 berechnet eine feste Vertrauensgrenze von 1,0, abzüglich 0,08 pro offener Frage, abzüglich 0,05 pro unüberprüfter Quelle – egal was Claude schreibt, das Tor begrenzt die Zahl arithmetisch. Andere A-Tore blockieren COMPLETE, solange benachbarte Probleme offen bleiben, verlangen ein nicht leeres Annahmeregister und stellen sicher, dass mindestens eine vorgeschlagene Maßnahme direkt auf die Ursache verweist. Es gibt insgesamt sieben solcher Tore.
  • Zwei zusätzliche Kontrollen kommen obendrauf. EQ1 verlangt, dass Ursachenknoten harte Beweise anführen (etwas anderes als INFERRED) oder eine Obergrenze von 60 % akzeptieren, während P7 die Obergrenze um 7 % senkt, je nachdem, wie viele feindliche Dimensionen als FAIL markiert sind – bis zu einem Mindestwert von 40 %.

    Insgesamt umfasst das Framework 19 Funktionen. Eine praktische Erinnerungshilfe: G-Tore reagieren auf das, was Claude schreibt, N-Tore auf das, was es weglässt, und A-Tore auf das, was es behauptet.

    Eine Verletzung wird zu einem Eintrag pending_guard_notes, der an den Anfang des Prompts der nächsten Iteration unter dem Titel ## SYSTEM QUALITY GUARD NOTES (aus der vorherigen Iteration) gesetzt wird, wobei eine ausdrückliche Anweisung gibt, zunächst alle Notizen zu bearbeiten. Das ist das Zwangsmittel. Die Schutzmechanismen blockieren das Modell nicht; sie sorgen dafür, dass seine früheren Verstöße als Erstes in der nächsten Runde gelesen werden. Höflichkeit seitens des Modells spielt keine Rolle – es ist die strukturelle Durchsetzung, die die Untersuchung vorantreibt.

    Ein Schritt zur Kontextfixierung (A7) wird ebenfalls am Ende jedes Prompt-Bauers ausgeführt, unabhängig davon, ob Fehler aufgetreten sind oder nicht. Dabei werden die ungelösten Widersprüche sowie unüberprüften Annahmen aus dem aktuellen Bericht Wort für Wort am Ende des nächsten Prompts wieder eingefügt. Dies kompensiert das bekannte Problem des Aufmerksamkeitsverlusts, bei dem ein Modell in Runde 3 auf eine Komplikation stößt und diese bis zur Runde 7 stillschweigend ignoriert.

    Vertrauensgrad als konkrete Zahl

    Die bedeutendste Gestaltungsentscheidung besteht darin, dass der Agent in der strukturierten Ausgabe jeder Iteration einem confidence: <int>-Wert auf einer Skala von 0 bis 100 zuweist. Diese Zahl dient nicht nur der Dekoration, sondern steuert tatsächliches Verhalten:

    • Unter 70 darf der Schleifenprozess nicht beendet werden. Der Pipeline erzwingt bis zu drei weitere Runden und fügt eine Hinweismeldung ein, in der angegeben wird, dass der Agent behauptet hat, fertig zu sein, obwohl der Wert unter dem Schwellenwert lag.
  • Bei einem Wert von 70 oder mehr mit status: COMPLETE und ohne open_questions wird der Übergang in die gegenseitige Überprüfungsphase (Red Team gegen Blue Team) ermöglicht.
  • Bei einem Wert von 95 oder mehr kann der Agent automatisch nicht zerstörerische Aktionen durchführen, wie z. B. einen Kommentar in Jira, einen Statuswechsel oder die Zuweisung eines Tickets. Bei Werten unter 95 bleiben diese Vorschläge als Entwürfe zurück, über die der Operator entscheiden muss.
  • Ein Rückgang von mehr als 10 % in zwei aufeinanderfolgenden Runden beendet den Zyklus mit dem Grund degrading. Da der Agent rückwärts entwickelt, stoppt er und informiert den Operator.
  • Die vollständige Zuverlässigkeitsentwicklung jedes Falls wird in ANALYSIS.md als Sparkline aufgeführt, z. B. 45% → 62% → 78% → 80%. Operator schauen kurz darauf, um zu beurteilen, ob das Ergebnis vertrauenswürdig ist, und der eigene Selbstverbesserungszyklus des Systems liest sie, um zu entscheiden, welche früheren Fälle mit einer anderen Anweisung erneut ausgeführt werden sollen.

    Drei Gründe, warum die Schleife nicht frühzeitig endet

    In der Darstellung erreichen drei Pfade ready_for_adversarial oder blocked, ohne dass die Schleife endet. Jeder dieser Pfade stellt eine Regel zur Fortsetzung der Ausführung dar: Das System ignoriert das Beendigungssignal und ersetzt es durch eine Schutzmaßnahme.

    • CONFIDENCE_FORCE wird angewendet, wenn status=COMPLETE und open_questions=[] gelten, aber der confidence-Wert unter 70 liegt. Das Modell erklärt einen Sieg, signalisiert jedoch durch die Zahl Zweifel. Es werden bis zu 3 weitere Iterationen erzwungen, die zum 20-Iterationen-Budget hinzugerechnet werden statt davon abgezogen.
  • CAUSAL_CHAIN_FORCE wird angewendet, wenn status=COMPLETE gilt und der Wert von confidence mindestens 70 beträgt, aber causal_chain_complete nicht wahr ist. Es handelt sich dabei um eine feststellung auf Symptomebene ohne nachvollziehbare Ursache. Dadurch werden bis zu 3 weitere Iterationen erzwungen, ebenfalls innerhalb des 20-Iterationen-Budgets.
  • G_BLOCKER_FORCE wird angewendet, wenn status=BLOCKED ist, obwohl erreichbare technische Quellen übersprungen oder aufgrund von Schlussfolgerungen als nicht verfügbar deklariert wurden – und nicht aufgrund eines tatsächlichen Fehlers. Eine Untersuchung kann nicht als blockiert beendet werden, solange nicht wirklich alles ausgeschöpft wurde. Es werden bis zu 3 weitere Fortsetzungen erlaubt, die ebenfalls innerhalb desselben Budgets gezählt werden.
  • Zusammen verhindern diese Mechanismen die drei häufigsten vorzeitigen Beendigungen: unsicherheit mit scheinbar hoher Zuverlässigkeit, Abschluss auf Symptomebene sowie vorgetäuschte Unverfügbarkeit.

    Der feindliche Prüfer

    Sobald der On-Call-Pipeline mit einer Zuverlässigkeit von 70 oder mehr angibt, fertig zu sein, geht die Steuerung an eine separate Claude-Aufrufung über, die einen adversarialen Prüfer einsetzt. Dieser Prüfer sieht den Prompt des Untersuchers nicht. Er erhält den Analysetext sowie eine Checkliste mit 15 Bewertungskriterien, von D1 bis D15, die die Gültigkeit der Annahmen, die Beweiskette, das kausale Denken, alternative Hypothesen und weitere Aspekte abdecken. Er gibt ein strukturiertes Urteil zurück:

    Es gibt drei Möglichkeiten, die Überprüfung abzuschließen:

    1. Kritische Probleme werden an den Untersucher zurückgegeben. Seine Notizen werden in die folgende Iteration als pending_guard_notes eingefügt und der Kreislauf beginnt erneut, wobei der Untersucher nun explizit erfährt, dass der Prüfer glaubt, etwas übersehen worden zu sein.
    2. Falls bei der ersten Überprüfung nur geringfügige Probleme vorliegen, wird der Kreislauf noch einmal durchlaufen, wobei die geringfügigen Kritiken mitübernommen werden.
  • Zwei aufeinanderfolgende Prüfungen, bei denen nur geringfügige Mängel vorliegen, oder eine Prüfung ohne Mängel beenden den Zyklus mit complete, und die Untersuchung wird veröffentlicht.
  • Dass der feindliche Schritt als separate Aufrufung mit einem anderen Prompt durchgeführt wird, geschieht absichtlich. Derselbe Modell in einer anderen Rolle erkennt einen bedeutenden Anteil an Selbstschmeichelei, der bei einer Iteration mit einem einzigen Prompt durchkommt. In einem Fall sank die Selbstsicherheitsanspruch zwischen der Untersuchung und ihrer Überprüfung von 90 auf 65, wobei der Prüfer genau auf die Annahme hinwies, die der Untersucher übersehen hatte.

    Eine zweite Überprüfung findet parallel dazu statt und ist anders aufgebaut: die Red-Team-Überprüfung (G5). Der adversäre Prüfer liest den gesamten Untersuchungsbericht, während dem Red-Team-Prüfer nur die ursprüngliche Problemstellung sowie das endgültige Fazit vorgelegt werden. Er erhält weder Zwischenschlussfolgerungen noch Beweismittelketten noch frühere Überprüfungsrunden. Anhand dieser beiden Informationen bildet er eine eigene Hypothese und weist darauf hin, wo die Ergebnisse der Untersuchung von dem abweichen, was ein Außenstehender vermutet hätte.

    Ziel ist die kognitive Isolation, nicht eine zusätzliche Abdeckung. Die adversäre Überprüfung fragt danach, ob die Untersuchung strukturell vollständig ist (D1 bis D15). Das Red Team fragt, ob zu dem Schluss auch ohne die Ausrichtung der Untersuchung gelangt werden könnte: Ist die Begründung tatsächlich am Problem ausgerichtet, oder stützte sich die Untersuchung auf eine frühe Erkenntnis und ließ den Schluss dieser Erkenntnis statt den Beweisen folgen? Beide Verfahren laufen als separate Claude-Aufrufe ab, und beide müssen bestanden werden, bevor ein Fall als COMPLETE markiert wird.

    Fünf ehrliche Wege zum Ende

    Der Untersuchungslauf kann aus fünf Gründen beendet werden. Der jeweilige Grund wird protokolliert und dem Operator angezeigt, wie in Zeilen wie diesen:

    exit=complete       confidence=89%  iterations=12  elapsed=13min
    exit=blocked        confidence=62%  iterations=4   elapsed=4min    (cannot proceed without info)
    exit=stalled        confidence=58%  iterations=7   elapsed=22min   (two rounds, zero new facts)
    exit=degrading      confidence=42%  iterations=5   elapsed=8min    (dropped >15% twice)
    exit=timeout        confidence=73%  iterations=18  elapsed=50min
    

    Von den fünf ist blocked der Zustand, auf den ein Operator am direktesten eingreifen kann. Der Agent hat explizite Blockierungen in unchecked_sources oder adjacent_problems erfasst, und ein Mensch muss anrufen, um sie zu beseitigen. Die anderen vier sind technische Fehlerarten, die vom System erkannt werden, ohne dass es blind erneut versucht.

    Was Claude von innen aus erreichen kann

    Die Grenzen der Tools spielen hier eine Rolle, denn die Zuverlässigkeitsregeln hängen teilweise davon ab, worauf Claude tatsächlich zugreifen kann. Eine Anweisung an den Agenten, seine unüberprüften Quellen zu prüfen, bedeutet nichts, solange die Tools zur Überprüfung nicht vorhanden sind. Jeder Pipeline startet einen Claude Code-Unterprozess mit --permission-mode bypassPermissions. In der Anfrage erscheint keine eingebettete Tool-Allowlist. Die Grenze ist das, was im Model Context Protocol (MCP)-Katalog definiert ist, und umfasst fünf externe Systeme:

    Mehrere Einschränkungen verdienen Erwähnung, da sie das Verhalten stärker beeinflussen als alles andere auf der Modellkarte:

    • Der Untersuchungspipeline ist ausdrücklich Bash verboten (--disallowedTools Bash), um git-Beziehungsprobleme zu vermeiden, da zwei Claude-Prozesse, die gleichzeitig an derselben Arbeitsbaumstruktur arbeiten, den Zustand beschädigen könnten. Die dev-agent-Pipeline verwendet zwar Bash, doch sie läuft in einem isolierten Arbeitsbaum.
    • Slack-Schreibwerkzeuge sind bei jeder Pipeline-Aufrufung blockiert. send_message_to_self und edit_message_to_self sind nicht zulässig, sodass Claude nicht selbst in Slack posten kann. Alle Ausgangskommunikationen erfolgen über das draft_response-Feld des strukturierten Kompletterungsberichts, den das System erst nach Überprüfung der Abbruchbedingungen veröffentlicht.
  • bypassPermissions bedeutet in der Praxis nicht uneingeschränkt. Der Framing-Prompt jedes Pipelines enthält feste Regeln. ANALYSIS.md kann bearbeitet werden, während message.json das nicht kann. Schreibvorgänge außerhalb von case_dir werden nach der Ausführung automatisch über git diff rückgängig gemacht. Schreibvorgänge in der Produktionsdatenbank erfordern gemäß der Anweisung die ausdrückliche Bestätigung des Operators. Die Sicherheit beruht auf der Framing-Schicht sowie einer Nachprüfung nach der Ausführung und nicht auf der Tool-Aufruf-Schicht.
  • Kaskadierte Fallback-Möglichkeiten sind in den Prompts enthalten und nicht hardcodiert. Wenn search_datadog_services mit einer Berechtigungsverweigerung zurückkommt, weist der Prompt den Agenten an, auf mcp__glean_default__search umzusteigen, wobei Jira-Schlüsselwörter als Ersatz verwendet werden. Die Fallback-Kette ist Teil der Umsetzung der SOP und hat keinen Platz im Laufzeitbetrieb.
  • Lesegerät-Tools laufen parallel, während Schreibtools nacheinander ausgeführt werden. Der Subprozess im Streaming-Modus analysiert tool_use-Ereignisse, sobald sie eintreffen, und leitet bis zu 10 gleichzeitige Lesevorgänge (Datadog-Anfragen, Glean-Suchen, Dateiabrufe) aus. Alle Schreibvorgänge – egal ob mit Nebeneffekten in Bash, Bearbeitungen, MR-Kommentaren oder Datenbankschreibvorgängen – erfolgen nacheinander. Eine einzige Untersuchungsrunde kann daher sechs parallele Protokollanfragen auslösen und dennoch die Schreibvorgänge geordnet halten.
  • Ein Skelett, vier Größen

    Jeder Pipeline im Harness verwendet eine Variante desselben Skeletts:

    Der Auftragsuntersuchungsprozess durchläuft bis zu 20 Iterationen. Die Analysephase umfasst bis zu 8 Schritte. In der Implementierungsphase wird das Skelett einmal ausgeführt, wobei der Build-Schritt dreimal wiederholt wird. Der Dev-Agent verfügt über keine innere Schleife und wird bei jedem Neustart einmal ausgeführt, doch mr-monitor verschiebt die Iteration effektiv nach außen: Jedes Mal, wenn der CI-Prozess bis zu drei Mal in Folge fehlschlägt, wird ein neuer Dev-Agent erstellt, bevor der Fall an einen Menschen weitergeleitet wird.

    Diese Gleichförmigkeit ist beabsichtigt. Die vier Schleifen existieren, weil die Probleme unterschiedlich sind, doch sie verwenden dieselbe Sprache: Das Vertrauensniveau wird als Zahl dargestellt, ein Ausstieg erhält einen Namen, die Selbstkritik erfolgt durch einen separaten Claude-Aufruf und eine Eskalation bedeutet eine DM in Slack im Thread, der den Fall auslöste. Ein Operator lernt diese Struktur einmal kennen und erkennt sie danach überall wieder.

    Die Selbstverbesserungsschicht

    Dies schließt den zuvor skizzierten Kreislauf ab, bei dem das Tool unter menschlicher Überwachung sein eigenes Substrat anpasst. Zwei Kreisläufe erledigen die Arbeit in unterschiedlichen Zeitskalen.

    Kreislauf 1 repariert die Strukturen Fall für Fall, von Lücken bis hin zu Pull Requests. Nach Abschluss einer Untersuchung sucht gap_analyzer nach Stellen, an denen das Tool schlecht abgeschnitten hat: übersprungenen Datensätzen, falschen Werkzeugauswahlen, Fehlern in der Pipeline, fehlenden Tests sowie schlecht formatierten Beschreibungen der Pull Requests. Kategorien- und Zuverlässigkeitsfilter reduzieren die Lücken, bevor finalize_case sie erhält. Wenn ein Fall vier Stunden lang unberührt bleibt, startet finalize_case patch_suggester in einem speziellen Arbeitsbaum und erstellt einen Pull Request gegen die eigene Codebasis des Tools, wobei Code-Dateien, Prompt-Vorlagen oder SOP-Abschnitte verändert werden. Die Fusion dieses Pull Requests erfordert einen Menschen – seine Erstellung nicht.

    Loop 2 stärkt das Verhalten in verschiedenen Fällen – von Fehlerrichtungen bis hin zu Vorschlägen für Standardprozesse. Am Ende jedes Falls erfasst der Schritt auto_retrospective, welche Fehlerrichtungen aufgetreten sind, und ordnet sie in drei Kategorien ein: Fehler bei der Datenerhebung (Wurden erreichbare Quellen übersprungen?), Verstöße gegen Qualitätskriterien (Kam eine Zuverlässigkeitsbehauptung im Widerspruch zu den Beweisen?) sowie Fehler bei der Vollständigkeit der Analyse (Hörte die Ursachenforschung vorzeitig auf?). Die Aufzeichnungen werden pro Projekt im Fallregister gesammelt. Jede Woche zählt ein Cron-Job mit dem Namen knowledge-updater die letzten zehn Fälle und erstellt bei Überschreiten eines Schwellenwerts einen Vorschlag für einen Standardprozess: 3 von 10 Fällen führen zu einem Vorschlag, 6 zu einer Warnung. Die Vorschläge sind konkret. Beispielsweise wurde „Überspringen der direkten DB-Anfrage“ diesen Monat 3 Mal registriert, weshalb der Standardprozess nun eine direkte Tabellensuche vor jeder Schema-Infereenz vorschreibt. Menschen prüfen diese Vorschläge und wenden sie an.

    Em – und hier wird aus Designgründen nichts automatisch angewendet, da automatisierte Entscheidungen, die ihre eigenen Fehler verstärken, ein bekanntes Risiko in selbstmodifizierenden Systemen darstellen.

    Daraus ergibt sich die Personalisierung. Die SOP enthält das Bild des Teams darüber, wie es Ermittlungen durchführt, und beginnt zunächst als allgemeines Muster. Im Laufe der Zeit erstellt der kb-Updater ein Profil der Fehlermuster eines Projekts anhand der Fälle und Wiederholungen. Bei 20 bis 30 Fällen ähnelt die SOP dieses Projekts nicht mehr dem allgemeinen Muster: Sie dokumentiert, welche Datensätze ständig übersprungen werden, welche Tools falsch verwendet werden und welche Ermittlungsansätze in dieser spezifischen Codebasis regelmäßig fehlschlagen. Die Projekte behalten separate SOPs bei, sodass die Fehler von Team A nur die Regeln von Team A beeinflussen und niemals Team B kontaminieren.

    Eine weitere Vorteil ist die Langlebigkeit. Ein Kontextfenster hält nur eine Sitzung lang, während eine zusammengeführte Codeänderung versioniert, überprüft, getestet und dauerhaft gespeichert wird. Ein SOP-Update, sobald es von einem Bediener genehmigt wurde, erscheint in allen nachfolgenden Untersuchungen. Das Substrat verbessert sich ständig, sodass jede neue Anwendung auf einer besseren Infrastruktur läuft als ihre Vorgängerin.

    Schicht 3: Zustand, der nach Neustarts erhalten bleibt

    Orchestrierung ist das, was bei jedem Ereignis abläuft. Ein persistenter Zustand hingegen bleibt bestehen, wenn ein Prozess neu gestartet wird, eine Maschine neu gebootet wird oder der Bediener von einem Laptop auf eine Cloud-VM wechselt. Die Infrastruktur bewahrt ihren Zustand in drei Schichten mit deutlich unterschiedlichen Eigenschaften auf:

    In-Memory-Caches sind klein und können neu erstellt werden. Sie befinden sich im Poller- sowie Keepalive-Prozess und werden nach einem Neustart aus dem lokalen Zustand wieder aufgebaut.

    Lokaler Zustand in ~/.harness/*.json ist die Betriebskarte. Die wichtigsten Dateien sind:

    • task-thread-map.json verknüpft einen Slack-Thread_ts mit einer JIRA-ID sowie einem absoluten task_dir-Pfad.
    • oncall_thread_map.json hat die gleiche Struktur für Fälle von On-Call-Ermittlungen.
    • monitored-mrs.json listet die MRs auf, die von Harness geöffnet wurden, zusammen mit ihrem CI-Zustand.
    • patch-pr-state.json speichert den Endzustand pro Fall (merged, no_gaps, failed usw.).
    • oncall-state.json enthält das Kill-Switch-Flag, das unter Schicht 6 behandelt wird.
    • socket-dedup enthält die Zeitstempel der kürzlich von Socket Mode verarbeiteten Nachrichten.

    Diese Schicht gehört zu einem einzigen Rechner und wird nicht geteilt. Sie bleibt nach einem Neustart erhalten, verschwindet jedoch, wenn der Rechner ausfällt.

    Git-synchronisierter Zustand ist die dauerhafte Schicht. Fallarbeitsräume unter case-workspaces// werden bei jeder bedeutenden Zustandsänderung gespeichert und in ein privates Git-Repository hochgeladen. Ein zweiter Rechner, der dieses Repository kloniert, kann denselben Gesprächskontext übernehmen, was die Kontinuität der Sitzung zwischen den Rechnern gewährleistet. Ein Operator kann über ein Laptop oder eine entfernte Entwicklungs-VM mit dem System chatten, ohne etwas zu verlieren.

    Die Speicherkonsolidierung wurde später zu einer dritten Aufgabe für den git-synchronisierten Zustand. Ein langes Gespräch erzeugt Dutzende von followup_transcript_*.md-Dateien. Wenn genügend Aktivität anfällt, liest ein Unteragent alle davon, schreibt TASK.md neu, um den zusammengefassten Zustand zu speichern, und der Workflow hört auf, rohe Transkripte für weitere Schritte einzuspeisen. Das Überspringen der Konsolidierung kann dazu führen, dass die Prompts den Kontextbereich des Modells überschreiten. Dabei besteht das Risiko, die Informationen durch zu starke Komprimierung zu verlieren – und das ist tatsächlich passiert: Ein 14KB großes Dokument schrumpfte auf 587 Bytes. Aus diesem Vorfall entstanden drei Schutzmaßnahmen: ein Prompt-Muster, das „UPDATE statt Umschreiben“ vorschreibt, eine Schutzfunktion für die Ausgabegröße, die Schreibvorgänge unter 50 Prozent der Eingabegröße verweigert, sowie ein Fallbezogener Lockfile, damit zwei Konsolidierungsabläufe nicht gleichzeitig stattfinden.

    Der Suchpfad von einem einzelnen Slack-Thread zu TASK.md des jeweiligen Falls ist kurz, aber es lohnt sich, ihn darzustellen:

    Sowohl die State-Dateien enthalten denselben Wert, nämlich einen absoluten Pfad zum Case-Arbeitsbereich. Jeder Workflow kommt früher oder später auf einen case_dir und arbeitet anschließend relativ dazu. Als der Wurzelordner des Arbeitsbereichs mitten im Projekt umbenannt wurde, handelte ein Migrationsskript dies dadurch ab, dass es die absoluten Pfade in beiden Karten umschrieb, während gleichzeitig der Ordner verschoben wurde.

    Eine dauerhafte State ist die Grundlage. Die nächste Frage lautet, was passiert, wenn darauf aufbauende Operationen fehlschlagen – die Antwort ist, dass das System daraus wiederherstellt und die Fehler nicht an den Operator weiterleitet.

    Schicht 4: sich selbst reparierende Schleifen

    Das ist die Schicht, in der das System nicht mehr nur als Benachrichtiger fungiert, sondern autonom wird. Sie enthält drei Schleifen, die jeweils ohne Beteiligung eines Operators abgeschlossen werden.

    Die ersten beiden sind vollständig autonom. Der dritte ist absichtlich nicht autonom. Um die Session des Identitätsanbieters aufzuladen, ist ein hardwaregebundener MFA-Anfragen erforderlich; daher stoppt der Agent und bittet um menschliches Eingreifen, wodurch eine 30-sekündige Ablaufanweisung in der privaten Nachricht übermittelt wird. Das ist durch Design ein menschlicher Eingriff und es besteht kein Funktionsdefizit.

    Diese Schleifen weisen eine subtile Eigenschaft auf: Jede verfügt über einen Wiederholungsbudget und kann erkennen, was sie bereits getan hat. Die automatische Korrektur in CI ist pro MR begrenzt, sodass drei aufeinanderfolgende Fehler an den Operator weitergeleitet werden. Die automatische Antwort der Überprüfer entfernt Duplikate nach Autor – das bedeutet, wenn die neueste Notiz des Bots in einer Diskussion jünger ist als die des Überprüfers, bleibt die Schleife still, da bereits eine Antwort existiert. Der Hilfsmechanismus des Identitätsanbieters ermöglicht ein 15-minütiges Genehmigungsfenster. Entfernt man diese Beschränkungen, können die Schleifen manchmal außer Kontrolle geraten. Frühzeitig führte ein MR mit einem unzuverlässigen Integrationstest dazu, dass innerhalb eines Nachmittags 80 sinnlose „CI korrigieren“-Kommitts vorgenommen wurden, bevor jemand es bemerkte.

    Aus Fehlern einzelner Operationen wiederherstellen

    Die oben beschriebenen Schleifen zur automatischen Behebung von Fehlern durch CI und zur Antwort der Überprüfer ermöglichen es, ganze Workflows von Anfang bis Ende wiederherzustellen. Darunter beruht jede primitive Operation, die fehlschlagen kann (ein Push, ein Rebase, ein Kommentar in einer MR, ein Branch-Reset), auf einem separaten, kleineren dreischichtigen Design zur selbstständigen Wiederherstellung pro Operation. Dieses unterscheidet sich vom dreischichtigen Sicherheitsmodell in Schicht 6, das sich auf Beschränkungen bezieht, also darauf, was der Agent tun darf. Dieses Modell hingegen bezieht sich auf die Wiederherstellung, also darauf, was passiert, wenn eine erlaubte Operation fehlschlägt. Die beiden sind leicht zu verwechseln, daher sollte man sie voneinander trennen.

    L1 ist ein deterministischer Schutzmechanismus. Er basiert auf Regeln und ist ständig aktiv, wodurch er in weniger als einer Sekunde reagiert. Für Push-Operationen wird _pre_push_rebase(repo_path, branch) verwendet: Unmittelbar vor dem Push führt das System git fetch origin sowie git rebase origin/ aus. Ein erfolgreicher Rebase ermöglicht den weiteren Ablauf des Pushs. Treten Konflikte oder ein fehlgeschlagener Fetch auf, stoppt L1 sicher ab, listet die konfliktierenden Dateien auf und leitet den Fall an L2 weiter. L1 darf absichtlich nicht kreativ sein – die Regeln bleiben klein genug, um verständlich zu bleiben, und kostengünstig genug, ohne Tokenverbrauch, um auf jede einzelne Operation angewendet zu werden.

    L2 ist ein selbstheilender Prozess mit Agentenfunktion. Wenn L1 es nicht schafft, die Situation zu beheben, startet das System eine eingeschränkte Claude-Sitzung (_self_heal_push_loop) und gibt ihr alle Informationen über den Fehler: den Git-Zustand, die konfliktspezifischen Dateien, die Aufgabe, die den Push ausgelöst hat, sowie einige strenge Einschränkungen. Man sollte niemals einen geschützten Branch mit Force-Push bearbeiten, niemals unpushte Commits ignorieren und niemals blind eine Seite im Konflikt wählen. Drei Einschränkungen sorgen dafür, dass dies sicher geschieht:

    1. Begrenzte Wiederholungsversuche. Jedes Problem erhält maximal 3 Versuche, und jede Aktion von Claude hat eine Zeitbegrenzung von 4 Minuten. Nach jeder Aktion von Claude wird der eigentliche git push erneut ausgeführt. Claudes Einschätzung, ob der Konflikt beseitigt ist, hat keinerlei Gewicht – nur ein Ausgabecode von 0 aus git push gilt als zuverlässige Information. Wenn der nächste Push einen nicht-nullen Wert zurückgibt, protokolliert der Loop den fehlgeschlagenen Versuch, unabhängig davon, wie zuversichtlich Claude den Erfolg behauptet hat.
    2. Zuverlässigkeitsgrenze von 70 oder mehr. Dies wurde unverändert aus der oncall_run-Konfiguration des on-call-Ermittlungsprozesses übernommen. Claude bewertet sich selbst bei jeder Aktion auf einer Skala von 0 bis 100; bei einem Wert unter 70 muss sie aufhören, anstatt weiter zu handeln. Eine selbst gemeldete Zuverlässigkeit unter dieser Grenze berechtigt nicht dazu, den Arbeitsbaum zu verändern.
  • Die Invarianz der Richtigkeit bleibt gewahrt. Ein erfolgreicher L2-Ausführungsvorgang bedeutet, dass tatsächlich ein Commit bei origin eingegangen ist. Das Antwort-System weiter im Prozessverlauf, das dem Reviewer eine „Erledigt, aktualisiert“-Meldung sendet, ist weiterhin direkt an den Ausgangscode git push mit Status 0 gekoppelt und niemals an irgendetwas, was Claude über sich selbst sagt. L2 kann dieses System nicht umgehen – es kann ihm nur einen echten Push zur Verfügung stellen.
  • L3 ist die Eskalation an den Operator. Wenn L2 aufgrund geringer Zuverlässigkeit abbricht oder alle Versuche aufgebraucht hat, sendet das System dem Operator eine Privatnachricht mit allen Informationen aus L2 – also jeder Versuch, jede von Claude durchgeführte Aktion, jede von ihm angegebene Zuverlässigkeitsbewertung sowie der finale git status. Der Operator startet dabei von einem vollständig dokumentierten Zustand aus und nicht von der Annahme, „etwas sei schiefgelaufen“. Die Zeitskala beträgt Stunden, nicht Sekunden.

    Zwei Prinzipien heben sich hervor:

    • L2 wird nur bei jedem einzelnen Vorfall genutzt und ist nicht ständig aktiv. L1 läuft bei jeder Übertragung, weil sie kostengünstig ist, während L2 Token verbraucht und erst dann aktiv wird, nachdem L1 versagt hat – somit hängt die Kostenentstehung vom seltenen Fall und nicht vom häufigen ab.
    • Die echte Referenz stammt direkt aus dem System. Unabhängig davon, was Claude über seine eigene Arbeit denkt, wiederholt die Validierung die tatsächliche Aktion: den Exit-Code von git push bei Übertragungen, einen tatsächlichen Testlauf bei Korrekturen sowie die Überprüfung des Push-Exit-Codes durch das Antwortsystem bei Rückfragen. Der Selbstbericht des Agenten wird niemals als echte Referenz angesehen, da das System dies gegenüber dem tatsächlichen System erneut überprüft.

    Dieser gleiche Wrapper eignet sich weitaus mehr als nur für Push-Operationen. Eine Operation erfüllt die Voraussetzungen, wenn drei Elemente vorhanden sind: eine einfache, regelbasierte Korrektur (L1), ein streng begrenztes automatisches Wiederholungsversuchssystem (L2) sowie eine klare Möglichkeit, die Aufgabe an einen Menschen weiterzugeben (L3). Diese dreiteilige Struktur unterscheidet zwischen einem autonomen Schleifenmodell, das von bereits bekannten Fehlern selbst wiederherstellt, und einem Modell, das sofort menschliches Eingreifen benötigt, sobald sich die Umstände ändern.

    Schicht 5: vier Orte, an denen man nachvollziehen kann, was passiert ist

    Das Framework schreibt in vier Überwachungsplattformen, wobei jede davon für einen anderen Leser optimiert ist:

    • Echtzeit-Updates über Slack. Während ein Agent läuft, wechselt eine Platzhalternachricht im Fallthread durch verschiedene Statusseiten (Reading TASK.md, anschließend Searching codebase, dann Drafting patch) und wird schließlich auf Done oder Failed: <reason> gesetzt. Dieser strömende Berichterstatter ändert die Nachricht direkt vor Ort, sodass die Threads nicht mit unnötigem Inhalt gefüllt werden.
    • Gestrukturierte Protokolle an den Orten ~/.harness/logs/agent.log und ~/.harness/logs/error.log. Jeder Neustart, jede Verteilung in einer Kette sowie jeder Wiederholungsversuch werden zu gestrukturierten Ereignissen, und diese Dateien werden dann durchsucht, wenn eine Diagnose erforderlich ist.
  • Slack-Directnachrichten für Terminal-Ereignisse, wie zum Beispiel die Eröffnung oder Fusion eines MR, das Ablaufen einer Session des Identitätsanbieters oder eine Eskalation im CI-Prozess. Sie gelten als Benachrichtigungen erster Klasse und werden im zugehörigen Fall threadet.
  • Ein Systemprotokoll unter ~/.harness/logs/system.log für quer durch alle Bereiche auftretende Ereignisse wie abgeschlossene Deploys, Watchdog-Aktionen und Sync-Zustände.
  • Eine Anzeigetafel wurde absichtlich weggelassen. Slack und grep dienen als Konsole. Eine Anzeigetafel würde eine weitere Ebene schaffen, um auf dem Laufenden zu bleiben, und ist das Erste, was Ingenieure nicht mehr lesen, sobald sie veraltet ist. Das Muster der strömenden Nachrichten liefert bereits ausreichend operative Signale, weshalb niemand eine Benutzeroberfläche darüber haben wollte.

    Überwachung der MCP-Verbindungen

    Fünf externe MCP-Server (Slack, Datadog, Jira und Confluence, Glean, GitLab) bilden die Grundlage dieses Systems. Jeder verfügt über seinen eigenen OAuth-Token, eine eigene Erneuerungsintervall und unklare Mechanismen bei Fehlern. Wenn ein Token unbemerkt abläuft, beginnt eine Claude-Sitzung mit der Antwort „Ich habe keinen Zugriff auf dieses Tool“, wobei der Operator keinerlei Fehler sieht.

    Ein kleines Tool namens mcp-watchdog schließt diese Lücke. Alle 10 Minuten prüft es den Health-Endpoint jedes MCPs und versucht einen sinnlosen Toolaufruf. Wenn dieser fehlschlägt, führt es Folgendes aus:

    1. Es versucht eine stille Token-Erneuerung.
    2. Falls die Erneuerung fehlschlägt, sendet es eine private Slack-Nachricht an den Operator mit dem Inhalt etwa „Der Atlassian MCP benötigt eine erneute Authentifizierung, klicken Sie hier.“
    3. Nach zwei aufeinanderfolgenden Trennungen (DISCONNECT_CONFIRM_THRESHOLD) wird die Situation mit einer :warning:-Reaktion im neuesten Ticket eskaliert.

    Fehlt diese Schleife, wird ein abgelaufenes Token fälschlicherweise als „der Agent ist heute dumm“ interpretiert – ein Fehler, dessen Diagnose äußerst schwierig ist, da agent.log niemals „Token ist abgelaufen“ anzeigt, sondern lediglich „Das Tool gibt ein leeres Ergebnis zurück“. Die Behandlung des Authentifizierungsstatus als zentrale Überwachungsinstanz wäre schon früher implementiert werden sollen.

    Schicht 6: Die Steueroberfläche des Operators

    Es handelt sich dabei um die am häufigsten genutzte Oberfläche, die ursprünglich jedoch unzureichend entwickelt wurde. Jede Interaktion des Operators erfolgt über einen einzigen Kanal, das Self-DM, und die Befehle lassen sich in sechs Kategorien einteilen:

    • Aufgabe: admin: task <jira> führt den gesamten Analyse-, Implementierungs- und MR-Prozess aus; durch Hinzufügen von --auto wird dieser Prozess unüberwacht ablaufen.
  • Auf Abruf: admin: oncall-run <url> startet eine Untersuchung aus einem Slack-Thread, während oncall-toggle die automatische Überwachung ein- oder ausschaltet.
  • MR-Kontrolle: admin: mr rescan prüft einen bestimmten MR erneut, und mr pause/resume schaltet den automatischen Antwortzyklus für diesen MR ein oder aus.
  • Pause: admin: oncall pause [Dauer] deaktiviert die automatische Zuweisung für eine Weile, und oncall resume/status beendet die Pause oder gibt den aktuellen Stand an.
  • Fertigstellen: admin: finalize <Fall> fasst die Lückenberichte zusammen und erstellt einen Verbesserungsantrag für das System selbst.
  • Registry: admin: register-thread verbindet manuell einen Slack-Thread mit einem Fall, während close-case diesen als abgeschlossen markiert.
  • Der Pausenschalter war am schwierigsten in die richtige Form zu bringen. In Version eins wurde alles pausiert, einschließlich der Admin-Befehle, wodurch ein Operator, der das System pausiert hatte, es nicht wieder in Betrieb nehmen konnte. In Version zwei wurde nur zu eng gepausiert, sodass der Befehl „den Lärm stoppen“ nicht berücksichtigt wurde. Das aktuelle Design basiert auf einem präzisen Unterschied: Die Pause schweigt die durch Aktivitäten anderer ausgelöste automatische Verteilung aus, niemals jedoch die eigenen Admin-Befehle oder Aktionen des Operators im Thread. Im Poller werden die Verteilungsorte in zwei Gruppen aufgeteilt. Eine Gruppe umfasst automatische Aktivitäten, die von anderen ausgelöst werden, und diese wird während der Pause blockiert. Die andere Gruppe umfasst die eigenen Absichten des Operators, und diese bleiben weiterhin aktiv.

    Die Kalibrierung war ein schwieriger Prozess. Kurz nach dem Versand der ersten Pause wurde automatisch eine Antwort auf einen Review-Kommentar in einem anderen Fall ausgelöst, ohne dass dies bemerkt wurde – schließlich sah es so aus, als wäre die Pause tatsächlich aktiv. Das aktuelle Design betrachtet die Pause als kontrollierte Stille: Es wird das System stillgelegt, während die Handlungsfreiheit des Bediners gewahrt bleibt.

    Ein Bild zeigt die Aufteilung:

    Die Regel lautet, dass die eigenen Aktionen des Bediners niemals gestoppt werden, während die Aktivitäten anderer Personen während einer Pause eingeschränkt sind. Das ist alles, was admin: oncall pause steuert.

    Zwei weitere Sicherheitsebenen

    Der Aus-Schalter für die Pause ist die auffälligste Sicherheitsmaßnahme – allerdings nicht die einzige. Zwei weitere funktionieren ohne jegliche Konfiguration während der Laufzeit, eine im Code und eine durch Deployment-Regeln:

    Die Allowlist für die Slack-Kanäle verdient mehr Aufmerksamkeit, als es auf den ersten Blick scheint. Ein knapper Vorfall zu Beginn des Projekts betraf einen falsch konfigurierten Slack-Token, der in die internen Teamkanäle schreiben konnte. Der Agent hätte direkt an die Teamkollegen schreiben können, ohne den Operator zu durchlaufen, und niemand hätte diese Beiträge von menschlichen Nachrichten unterscheiden können. Heute wird jede ausgehende chat.postMessageAktion vor dem Versand anhand einer Allowlist auf das Ziel überprüft. Das einzige erlaubte Ziel ist die private Nachricht des Operators sowie eine fest codierte Kanal-ID für das eigene Gespräch des Operators mit dem Bot. Wenn der Agent einen Teamkollegen erreichen möchte, gibt er den Inhalt an den Operator zurück, der entscheidet, ob er weitergeleitet wird. Diese architektonische Regel hat sich immer wieder bewährt: Der Agent kommuniziert nach außen nur über den Posteingang des Operators.

    Jenseits der sechs Schichten: Was macht es für die Produktion geeignet

    Die sechs Schichten bilden die tragende Struktur. Mehrere Mechanismen liegen außerhalb dieser Klassifizierung, sind aber genauso wichtig. Sie unterscheiden ein gut funktionierendes Harness von einem, das ohne Unterbrechungen läuft.

    Mehrere Projekte aus einer YAML-Datei routen

    Derzeit laufen zwei Projekte über das Harness, jeweils mit eigenen Betriebsregeln. Die Hinzufügung eines dritten Projekts erfordert keinerlei Codeänderungen, lediglich einen neuen Eintrag in agent-config/projects.yaml:

    projects:
      team-a:
        sop: "team-a/SOP.md"
        kb: "team-a/KNOWLEDGE-BASE.md"
        dfr_threads_dir: "team-a/threads"
        task_dir: "task-workspaces"
        slack_channels: ["support-channel-a", "support-channel-b"]
        jira_prefixes: ["AAA", "BBB", "CCC"]
      team-b:
        sop: "team-b/SOP.md"
        kb: "team-b/knowledge-base/kb.md"
        dfr_threads_dir: "team-b/threads"
        task_dir: "task-workspaces"
        slack_channels: []
        jira_prefixes: ["XX", "YY"]
    

    Die Funktion resolve_project() leitet jedes eingehende Signal nach Priorität weiter: Zuerst das Jira-Präfix, anschließend der Slack-Kanal und danach die Standardroute. Jedes Projekt verfügt über seine eigene SOP-Datei, KB-Datei sowie Verzeichnisstruktur, und die automatische Auswahl der entsprechenden Dokumente für Ermittlungen ermittelt je nach dem zugehörigen Projekt automatisch die richtige SOP- und KB-Datei. Dadurch kann eine einzige Harness-Instanz zwei Teams mit jeweils unterschiedlichen Regeln bedienen.

    Dieses Muster lässt sich generalisieren. Jede Organisation mit mehreren Produktteams unter einem Harness kann denselben Ansatz nutzen: deklarative Konfiguration ohne projektbezogene Code-Branches. Zudem sorgt dies für eine klare Disziplin, da alles, was spezifisch für ein Projekt ist, in der SOP-Datei dieses Projekts aufbewahrt werden muss und niemals im Code, da der Code gemeinsam genutzt wird.

    Fortsetzung einer Sitzung auf einem der Geräte

    Der Operator arbeitet mit zwei Maschinen – einem Laptop und einer langlebigen Cloud-VM im Rechenzentrum – und eine Claude-Sitzung muss auf beiden wieder aufgenommen werden können. Das ist schwierig, weil Claude Code standardmäßig den Sitzungszustand auf der lokalen Festplatte speichert, sodass der Sitzungszustand nach einem Neustart an einem anderen Ort nicht mehr sichtbar ist.

    Das Tool löst dieses Problem mit einem durch Git unterstützten Sitzungsspeicher:

    Der Mechanismus besteht aus drei Teilen:

    1. Sitzungen werden in Git verfolgt. Die .jsonl- und .meta-Dateien jeder Sitzung werden bei jeder bedeutenden Zustandsänderung gespeichert und in ein privates Repository hochgeladen, wobei dieses Repository als einzige Quelle der Wahrheit dient.
    2. Ein Hintergrundprozess läuft alle 5 Sekunden auf jeder Maschine, um die lokale Kopie aktuell zu halten. Er lädt den Kontext im Voraus herunter, sodass beim Wiederaufnehmen einer Claude-Sitzung keine Netzwerkverzögerungen bei jeder Tasteneingabe entstehen.
  • Die Session-UUIDs werden durch den Arbeitsverzeichnispfad abgeglichen. Wenn Claude an einer bestimmten Stelle gestartet wird, durchsucht der Hook session-start.sh die lokalen .meta-Dateien nach dem Eintrag, der für dieses Arbeitsverzeichnis gespeichert ist, und holt dessen UUID ab. Somit folgt eine UUID dem Benutzer von Maschine zu Maschine.
  • Der Synchronisierungs-Hook user-prompt-submit.sh wird nach jeder Anfrage an Claude ausgelöst und führt in strenger Reihenfolge drei Operationen durch, die mit flock seriellisiert werden:

    1. Abrufen und Zurücksetzen. Durch Ausführung von git fetch origin main && git reset --hard bleibt die lokale Datei active.jsonl mit der Transkription der anderen Maschine erhalten.
    2. Extrahieren Sie den Delta-Unterschied vor dem Überschreiben. Es liest das Wasserzeichen ab, das angibt, wie viele Zeilen bereits von der anderen Maschine eingefügt wurden, extrahiert nur die neueren Zeilen und speichert sie in einer temporären Datei. Dies muss vor Schritt 3 erfolgen. Ein früherer Fehler führte dazu, dass die beiden Schritte vertauscht wurden, wodurch die Einpflegung weiterhin die eigene Transkription der Maschine erneut las.
    3. Senden Sie Ihre eigene Transkription. Es kopiert die lokale Claude-Sitzungstranskription in active.jsonl, führt einen Commit durch und sendet die Datei hoch, bevor es das Wasserzeichen auf die lokale Zeilenanzahl setzt.

    Sobald die Gruppe die Kontrolle freigibt, erreicht der in Schritt 2 gespeicherte Delta-Unterschied Claude als Systemnachricht, die angibt, dass die andere Maschine seit dem letzten Synchronisieren N Nachrichten hinzugefügt hat. Der nächste Prompt erhält anschließend den Kontext beider Maschinen.

    Eine separate .linecount-Datei enthält die tatsächliche Gesamtzahl der Zeilen, da wc -l irreführend ist, sobald eine Transkription zum Hochladen gekürzt wurde. Um unter der 25-MiB-Limitierung von git zu bleiben, werden Transkripte auf 1.000 Zeilen gekürzt, bevor sie hochgeladen werden. Ohne .linecount würde jede Kürzung das Wasserzeichen zurücksetzen und die Wiedergabe würde bereits gesehene Tausende von Zeilen erneut liefern.

    Die durch flock serialisierte git-Operation verhindert eine Korruption von index.lock durch gleichzeitige Hochladevorgänge – was bereits einmal geschah, als ein blockierter Sitzungsstart mit dem Ausführen eines Meta-Enrichers kollidierte. Ein drosselbarer Mechanismus, standardmäßig auf 30 Sekunden eingestellt, überspringt bei schnellen Tasteneingaben die vollständige Synchronisierung, sodass es keine git-bezogene Verzögerung pro Zeichen gibt.

    Die übergeordnete Idee, die man sich aneignen sollte, ist Git als Zustandsspeicher. Hier dient Git nicht nur als Code-Repository, sondern fungiert auch als langlebiger Log im Stil von CRDTs für den Zustand der Agenten, wobei die beiden Maschinen des Operators als letztendlich konsistente Leser fungieren.

    Die Gedächtniskonsolidierung hängt eng mit der Kontinuität über verschiedene Maschinen hinweg zusammen. Der Schritt „memory_consolidate“, der die Transkripte in saubere DATEIEN TASK.md und ANALYSIS.md umschreibt, wird nicht nach einer Zeitvorlage ausgeführt. Ein separates Service namens consolidate_trigger prüft bei jeder Runde vier Signale: einen Commit-SHA oder ein Entscheidungsverb im Transkript, einen gerade geschlossenen Überprüfungsthread, ein Arbeitsbereich mit mehr als 5 KB an angesammelten Transkripten oder mindestens eine Stunde seit der letzten Konsolidierung. Jedes dieser Signale kann sie auslösen. Der Grund dafür ist, dass Informationen nach einer Entscheidung aufhören, sich anzuhäufen – und nicht nach einer festgelegten Zeitdauer. Gemeinsam ermöglichen die vier Signale eine Einschätzung, ob das Gespräch eine natürliche Pause erreicht hat, sodass der Operator dies nicht extra angeben muss.

    Worktrees sind mehr als nur ein Implementierungsdetail

    Zuvor wurde gesagt, dass alles in Worktrees abläuft. Das erfordert eine Erklärung, denn die dahinterstehende Entscheidung hat tatsächlich große Bedeutung.

    Das ursprüngliche Design sah vor, dass der Agent einen Diff ausgibt und das Harness in dem Haupt-Repo-Checkout git apply ausführt. Dadurch traten zwei wiederkehrende Fehlermuster auf:

    1. Brüchige Diff-Überschriften. Claude gab einen Diff aus, dessen Zeilennummern zum Zeitpunkt der Erstellung korrekt waren, aber nach dem Eintreffen eines gleichzeitigen Commits falsch wurden, wodurch git apply den gesamten Patch ablehnte.
    2. Ein blockierter Hauptbaum. Wenn git apply --check auf halbem Weg fehlschlug, blieb das Haupt-Repo teilweise angewendet, und der nächste Vorgang im selben Repo übernahm diesen „schmutzigen“ Baum.

    worktree_manager ist die Lösung. Vor jedem Agenten, der Code ändern muss, erstellt er einen abgeschirmten Arbeitstree unter ~/.worktrees/case-<case_id>-<stamp>/, der von einer sauberen Branch aus heruntergeladen wird. Claude arbeitet dort mit seinen eingebauten Bearbeitungs- und Schreibwerkzeugen, wodurch git apply sowie der Hin- und Rückweg über Diff-Texte entfallen. Es verhält sich wie ein menschlicher Ingenieur: neue Branch, neuer Download, anschließend Bearbeitung, Commit und Push.

    Sobald der Pull Request integriert oder geschlossen wird, wird der Arbeitstree entfernt. Ein teilweiser Fehler betrifft nur diesen Arbeitstree und lässt die Haupt-Branch sowie andere gleichzeitige Fälle unberührt.

    Deshalb sind mehrere Agenten auf derselben Maschine überhaupt sicher. Ohne Arbeitstrees pro Fall würden zwei Fälle im selben Repository um den Zugriff auf den Dateisystemzustand konkurrieren. Mit ihnen sind die Fälle von vornherein isoliert.

    The harness behebt seine eigenen Pull Requests

    Die Quelle des Harnesses befindet sich in einem privaten GitHub-Repository. Wenn der Gap-Report, das Finalisieren sowie der MR-Lauf eine Verbesserungsvorschlag für den Harness selbst ergeben, durchläuft dieser Vorschlag dessen eigene CI-Pipeline mit pytest, Lintern und einer Python-Syntaxprüfung. Ein separates Service namens pr-ci-fixer kümmert sich auf dieselbe Weise um eventuelle Fehler wie mr-monitor um Fehler in Ticket-MRs.

    Dadurch erfolgt eine Selbstbehebung sowohl für den für Tickets geschriebene Code als auch für den Code bezüglich des Harnesses. Angenommen, ein MR fügt einen neuen Wiederholungsversuchsbudget hinzu und bricht dabei einen bestehenden Test, wodurch die CI fehlschlägt. pr-ci-fixer erkennt dies, startet eine Claude-Sitzung in einem Worktree, behebt das Problem und pusht erneut, sodass der menschliche Reviewer einen grünen MR vorfindet, der direkt eingemergt werden kann. Der Kreislauf hat sich bereits mehrfach bei tatsächlichen Änderungen am Harness geschlossen.

    Die Überprüfung, ob der Loop auf die eigene Quelle des Harnesses verweist, ist die beste Methode, um festzustellen, ob er tatsächlich den Namen „Production“ verdient. Ein Agent, der seine eigenen Pull Requests erfolgreich abschließen kann, ist vermutlich geeignet, auch die von anderen zu betreuen.

    Fünf Testebenen für fünf Fehlerkategorien

    Der Harness enthält 4.656 Testfunktionen in insgesamt 126 Dateien. Diese Zahl sagt wenig ohne ein Framework, das angibt, was jeder Test überprüft – schließlich sind Tests nicht alle gleich.

    Das L0 bis L4-Framework ordnet jeden Test einer der fünf Ebenen zu, je nachdem, was er überprüft:

    • L0, reine Grenzüberprüfung. Es werden keine Mocks verwendet, es gibt weder Dateisysteme noch Eingabe/Ausgabe-Operationen. Hier werden reine Logiken wie Gate-Funktionen, JSON-Extraktion sowie Berechnungen zum Konfidenzrahmen überprüft; alles, was bei L0 funktioniert, funktioniert auch in jedem Deployment-Umfeld.
  • L1, Dateiisolierung. Es wird ein echtes tmpdir-Dateisystem sowie echte Threads verwendet, wobei pathlib unmocked bleibt. Hier treten Rennen zwischen gleichzeitigen Schreibvorgängen sowie Fehler bezüglich der Atomität von Zustandsdateien auf, die L0 aus Konstruktionsgründen nicht erkennen kann.
  • L2, Dienstisolierung. Echte Unterprozesse laufen gegen gemockte externe APIs (Slack, GitLab, Claude). Fehler in der Zusammenarbeit der Komponenten – einschließlich der Routenverwaltung, der Payload-Schemata sowie der Übergänge in den Zustandsdateien – kommen auf dieser Ebene zum Vorschein, da L1 nur das Verhalten des Dateisystems isoliert.
  • L3, Sandbox. Über einen Unterprozess wird ein echtes git-Repository erstellt, während die Antworten von Slack und Claude gefälscht werden. Ganze Workflow-Sequenzen laufen unter Verwendung eines echten git-Objektmodells ab, beispielsweise bei der Registrierung von Aufgaben, dem Start der Implementierungsphase sowie der abgewarteten Bestätigung.
  • L4, Integrationsmatrix. Ihre sechs Unterkategorien sind Akteurverträge (L4a), die Matrix zur Verwaltung nach Rolle (L4b), Zustandskonsistenz über mehrere Ausführungen hinweg (L4c), Kettenverbindungen zwischen Diensten (L4d), externe Akteurrollen (L4e), methodische Lücken (L4f) sowie zeitliche Abfolgen (L4h und L4i).
  • L4g, Wiederausführung in der Produktion. Dreizehn Tests stellen spezifische Produktionsvorfälle als Regressionstests nach. Sie werden nach dem Auftreten eines Fehlers in der Produktion verfasst, wobei der genaue Akteur, der Kontext sowie der Zustand, der ihn ausgelöst hat, erfasst werden; sie werden dauerhaft aufbewahrt, damit der Fehler nicht erneut auftritt.
  • Die L4g-Tests sind am aufwendigsten in der Erstellung und am wertvollsten in der Aufrechterhaltung. Sie enthalten institutionelles Wissen über Fehlermuster, die bis zu ihrem erneuten Auftreten unsichtbar bleiben.

    Ein Stilleitfaden, der verhindert, dass die Ausgabe wie maschinengeschrieben wirkt

    Jeder Pull Request durchläuft vor der Einreichung einen weiteren Filter – es handelt sich dabei um eine speziell erstellte Prozessanleitung, damit das Ergebnis nicht wie von KI erzeugt aussieht. Die Regeln sind eindeutig:

    • Ganzekeine Angabe einer KI. Kein Co-Authored-By: Claude, kein „Generated with Claude Code“ und kein [bot]-Suffix in den Commit-Nachrichten. Der Pull Request gehört dem Team und nicht dem Modell.
    • Keine auszufüllenden Überschriften. Vorlagen wie „Was dieser Pull Request leistet / warum wir ihn benötigen“ sind verboten. Die Beschreibung besteht aus kontinuierlichem Text mit einer konkreten Changes:-Aufzählung, geschrieben auf die Weise, wie es ein Ingenieur von Hand tun würde.
    • Keine fettgedruckten Inline-Tags. Ein Root cause:-Label gefolgt von einem Absatz ist nicht zulässig, genauso wenig wie Fix: und Risk:. Fettgedruckte Tags sind der zuverlässigste Hinweis auf maschinengenerierte Texte.
  • Die Arbeitsnachweis-Generierung muss über einen Befehl reproduzierbar sein. „Lokal verifiziert“ zählt nicht. pytest tests/test_foo.py::test_bar -v mit dem tatsächlichen Befehl sowie den entsprechenden 3 Ausgabezeilen schon.
  • Zur Zeit der Erstellung der MR-Beschreibung wird die SOP in den Prompt zur Patch-Generierung eingefügt. Sie steht getrennt vom Prompt-Template da, weil die Operator sie verwalten: Editieren Sie sie jederzeit nach Belieben – die nächste MR folgt dann den neuen Regeln, ohne Neustart des Daemons oder Änderungen am Code. Selbst der Gap-Loop kann Vorschläge zur Anpassung der SOP einreichen, sodass Metaregeln darüber, wie der Agent MRs erstellt, als eigene MRs gegenüber dem Dateiinhalt, in dem sie gespeichert sind, weitergegeben werden.

    Dies ist wichtiger, als es klingt. Nichts zerstört das Vertrauen in einen automatisierten MR-Prozess schneller, als wenn das Team bemerkt, dass alle Beschreibungen wie maschinengeneriert wirken. Sobald sich dieses Bild festsetzt, steigt der Merge-Bar-Wert, die echten Signale des Mitarbeiters gehen unter, und die Operator beginnen, die Beschreibungen von Hand umzuschreiben – was dem Zweck widerspricht. Die Stilrichtlinien sorgen dafür, dass die Beschreibungen sich mit menschlich verfassten Texten vermischen, wodurch der Prozess weiterhin funktioniert.

    Ein Ticket, ein MR – von Anfang bis Ende

    So verläuft der Ablauf, wenn ein echtes Ticket eintrifft. Die Zeiten sind ungefähre Werte.

    Insgesamt dauert dieser Prozess für ein kleines oder mittelgroßes Ticket in der Regel 30 bis 90 Minuten. Die gesamte aktive Beteiligung des Operators beträgt in der Regel weniger als 5 Minuten: Bestätigung der Analyse, gegebenenfalls Leitung bei unklaren Entscheidungen und optionales Klicken auf „Merge“.

    Es sammeln sich Untersuchungen. Nach 30 Fällen gibt es 30 gap_report*.md-Dateien, jede mit einer Anmerkung darüber, was das LLM für verbessert halten könnte. Ein admin: gap-patterns-Ablauf alle zwei Wochen gruppiert sie in wiederkehrende Fehlermuster, und die anschließende Endabnahme wandelt die größten Cluster in MRs um, die sich auf das harness-Repo richten – dadurch verbessert sich das harness selbst. Das ist der in die Tat umgesetzte Selbstverbesserungskreislauf.

    Vergleich mit anderen Harnesses

    Mehrere andere Systeme haben eine ähnliche Struktur wie dieses Harness. Die Tabelle zeigt, wo es Überschneidungen mit ihnen gibt und wo es abweicht:

    Wo dieses Harness ungewöhnlich tief geht:

    • Verriegelung der Hypothese vor der Bearbeitung. Claude legt mindestens sechs Hypothesen vor, bevor eine Untersuchungsrunde ausgeführt wird, was eine nachträgliche Rationalisierung verhindert. Keines der im Vergleich stehenden kommerziellen Tools bietet etwas Ähnliches.
    • Ein Qualitätskontrollrahmen mit 19 Funktionen und mechanischen Vertrauensgrenzen. Das Vertrauen wird durch eine feste Formel begrenzt, die bei 1,0 beginnt und pro offener Frage um 0,08 sowie pro unüberprüfter Quelle um 0,05 abnimmt – es handelt sich somit um mehr als nur eine selbst gemeldete Zahl. Zu den drei Kontrollkategorien für logische Konsistenz, strukturelle Vollständigkeit und Aussagegrenzen kommen noch dimensionsspezifische Strafmaßnahmen hinzu. Keines der im Vergleich stehenden kommerziellen Tools bietet dies.
  • Selbstverbesserung durch Anpassung der eigenen Codebasis des Tools. Die Mängelberichte werden zu gefilterten Pull-Requests gegen den Code des Tools sowie die dazugehörigen Anleitungen und Standardverfahren. Der nächstliegende akademische Vergleichsfall, SICA (arXiv 2504), berichtet zwar von Leistungsverbesserungen durch Benchmarks, bleibt jedoch eine offline durchgeführte Forschung, und kein Produktionsystem führt autonome Änderungen am eigenen Code des Tools durch.
  • Anpassung der Standardverfahren je nach Projekt. Die Standardverfahren jedes Projekts entwickeln sich aus den dortigen Fehlermusterzahlen, und innerhalb von 20 bis 30 Fällen erfassen sie die wiederkehrenden Untersuchungserfolge in diesem Bereich. Im Vergleich wurde kein anderes Produktionstool gefunden, das dies tut.
  • Mehre-Agenten-Orchestrierung mit expliziten Übergaben. Die Kette läuft von oncall_run über case_followup, gap_analyzer, finalize_case bis zu dev-agent. Jeder Übergang verfügt über ein definiertes Übergabeschema sowie einen isolierten Arbeitsbaum, wodurch die Arbeit unter den Agenten aufgeteilt wird anstatt auf einen einzigen Agenten abgewälzt zu werden.
  • Zustandssynchronisierung zwischen Maschinen durch wasserzeichenbasierte Einbettung über Git. Eine Claude-Sitzung wird auf einem Laptop oder einer entfernten VM mit ihrem vollständigen Kontext fortgesetzt, ohne dass jemand etwas manuell synchronisieren muss.
  • Integrierte CI-Auto-Behebungs- und Reviewer-Auto-Antwortschleifen. Devin, ein kommerzielles Produkt, ist das einzige mit integrierter, vergleichbarer Selbstheilungsfunktion, während alle anderen Frameworks eine individuelle Anpassung erfordern.
  • Dort, wo es absichtlich schlicht gehalten ist:

    • Es gibt kein formelles Bewertungswerkzeug. Das LLM wird nicht anhand von Benchmarks bewertet, sondern die Rückmeldungen aus der Produktion dienen als Bewertungsgrundlage.
    • Es gibt kein Dashboard, nur Slack und Logs.
    • Es gibt keinen Kostenanzeiger. Die Token-Nutzung wird nur ungenau erfasst und nicht im Dashboard angezeigt; die Kosten werden stattdessen durch die Bestätigungsprozesse durch Menschen begrenzt.

    Was bleibt und der Meta-Zyklus

    Das Werkzeug entwickelt sich weiterhin, es gibt noch offene Punkte in Bezug auf die Abdeckung des autonomen Zyklus, die Kostenmessung sowie ein formelles Bewertungswerkzeug – doch keines davon stellt ein Hindernis dar. Es erfüllt bereits seinen ursprünglichen Zweck: Slack-Signale in zusammengeführten Code umzuwandeln, Rückmeldungen von Reviewern über Nacht zu beantworten, den CI-Prozess selbstständig zu reparieren und seine eigenen Verbesserungen in die nächste Version einzubringen.

    Die eigentliche Lektion ist der Meta-Zyklus. Jede „Scar“ im Harness existiert, weil ein echtes Ticket Druck auf sie ausgeübt hat. Entwicklung und Betrieb bilden einen Zyklus, der mit zwei verschiedenen Geschwindigkeiten abläuft. Das Produktionssignal fließt zurück zu der Quelle des Harnesses selbst – der Agent entwirft eine Korrektur, ein Mensch prüft und integriert sie, wodurch eine weitere Gruppe von Fehlern der Beseitigung näherkommt.

    Das System ist noch nicht fertig. Ein Produktionsharness, der glaubt, fertig zu sein, hat aufgehört, „Scars“ anzusammeln – was wiederum bedeutet, dass er nicht mehr unter echter Last läuft.

    Bauen Sie zunächst den Zyklus auf und machen Sie ihn anschließend autonom. Ein autonomes System ohne Rückkopplungspfad zu seinem eigenen Substrat führt einfach dieselben Fehler nur schneller und in größerer Anzahl aus – deshalb ist die Reihenfolge wichtig.