Claude und Codex im adversarialen Code-Review: Relay-Loop mit accepted/rejected-Verdikt
Zwei KI-Modelle, die sich abwechselnd Code bauen und gegenseitig reviewen
Ein Modell baut, das andere reviewt — und bei jedem Accept tauschen sie die Rollen. Wie ich eine Relay-Schleife zwischen Claude und Codex gebaut habe und welche Sandbox-Falle dabei lauerte.
Ich habe eine Relay-Schleife gebaut: ein Modell baut, das andere reviewt — und bei jedem Accept tauschen sie die Rollen. Das klang nach einem eleganten symmetrischen System. Was ich tatsächlich bekam, war erst eine starre Bridge, dann ein Sandbox-Hang, und schließlich drei Runden live: Codex baut, Claude reviewt, lehnt ab, Codex überarbeitet, Claude nimmt an, die Rollen tauschen. So lief es. Was dazwischen lag, ist der interessante Teil.
Der asymmetrische Anfang
Die erste Version meiner Bridge zwischen Claude und Codex hatte ich fest verdrahtet: Claude baut, Codex reviewt. Oder umgekehrt, je nach Konfiguration. Einmal gesetzt, unveränderlich für die gesamte Session.
Das hat funktioniert. Eine Seite produziert Diff, die andere gibt Verdikt. Das ist bereits nützlich, weil zwei Modelle unabhängige Urteilsachsen haben — was Claude übersieht, sieht Codex, und umgekehrt. Die Kombination ist robuster als ein einzelnes Modell, das seinen eigenen Output bewertet: andere Trainingsgewichte, andere blinde Flecken, anderes Gewichtungsmuster bei Sicherheit versus Lesbarkeit.
Aber die fixe Rollenverteilung verschenkt die Hälfte der Kapazität: der Reviewer kann auch bauen, der Builder kann auch reviewen. Die starre Zuweisung ist keine strukturelle Notwendigkeit, sondern eine Vereinfachung der ersten Iteration. Nützlich zum Beweisen des Konzepts — unzureichend für einen echten Relay-Betrieb. Das war der Ausgangspunkt für den nächsten Schritt.
Reviewer-Symmetrie
Jedes Modell kann beides: bauen und reviewen. Der Reviewer wird automatisch als das jeweils andere Modell gewählt — Codex reviewt Claude-Output, Claude reviewt Codex-Output. Das lässt sich über ein Flag überschreiben, aber der sinnvolle Default ist das Gegenteil des Builders.
Was das konkret bedeutet: Codex reviewt rein auf Basis des übergebenen Textes, ohne eigene Datei-Zugriffe — read-only im buchstäblichen Sinn. Claude ebenso, wenn es als Reviewer läuft. Keiner der beiden greift in dieser Phase schreibend ins Repository ein. Das ist eine bewusste Trennung: die Review-Phase erzeugt kein neues Artefakt, nur ein Urteil. Verdikt accept, reject, oder rework mit kommentierter Begründung. Das Urteil ist der Output, nicht ein neuer Diff.
Die Symmetrie macht das System robuster gegen Bias: wenn Claude immer baut und Claude immer reviewt, lernt das System seine eigenen Muster als Standard. Wechselnde Rollen erzwingen, dass jeder Output durch eine andere Urteilsachse geht. Das ist kein kosmetisches Detail — es verändert, welche Fehler überhaupt sichtbar werden.
Die Relay-Schleife
Beide Modelle bauen abwechselnd. Der Reviewer beurteilt den inkrementellen Diff — nicht die gesamte Codebasis, nur was sich seit dem letzten Accept geändert hat. Das hält den Kontext des Reviewers klein und das Urteil fokussiert.
Bei accept tauschen die Rollen: der bisherige Builder wird Reviewer, der bisherige Reviewer übernimmt das nächste Bau-Segment. Bei reject bleibt der Builder in seiner Rolle und überarbeitet, bevor weitergegeben wird. Das verhindert, dass ein schwacher Diff in die nächste Runde geht und sich Fehler aufschichten.
Ich habe das live über drei Runden getestet: Codex baute eine Komponente, Claude reviewte und lehnte mit konkretem Befund ab — zu wenig Fehlerbehandlung, ein konkreter Fall unberücksichtigt. Codex überarbeitete, Claude nahm an. Die Rollen tauschten — Claude baute den nächsten Schritt, Codex reviewte und gab ohne Einwände frei. Die Schleife läuft. Was mir dabei auffiel: die Ablehnungsrunde war produktiver als ein blindes Durchwinken es gewesen wäre. Das Rework hat den Diff tatsächlich verbessert, nicht nur formal den Reviewer besänftigt. Drei Runden, ein Reject, ein Rework, zwei Accepts.
Die Schleife läuft synchron: ein Modell ist aktiv, das andere wartet. Das ist langsamer als paralleles Bauen, aber einfacher zu koordinieren, deterministisch in der Reihenfolge und ohne Merge-Konflikte. Für eine erste produktive Implementierung ist synchron der richtige Kompromiss.
Die Sandbox-Falle
Der Codex-Reviewer läuft read-only. Keine Schreibzugriffe, keine Commits, nur Lesen und Urteilen. Klingt harmlos. Ist es nicht, wenn approval_policy nicht explizit gesetzt ist.
Ohne approval_policy=never läuft Codex auf Windows in eine %TEMP%-Probing-Schleife. Das Modell versucht, seinen Arbeitsbereich zu sondieren, schreibt dabei in %TEMP%, und friert ein, wenn der Sandbox-Modus das blockiert. Der Hang ist still — der Wrapper-Output ist oft leer oder gepuffert, nichts deutet auf das Problem hin. Der Hängepunkt zeigt sich erst im Rollout-Log unter ~/.codex/sessions/: der letzte function_call-Eintrag ohne zugehörigen function_call_output ist der Moment, an dem der Prozess eingefroren ist.
Die Lösung ist zweiteilig und nicht intuitiv: Builder brauchen Schreibzugriff (danger-full-access für einen isolierten Wegwerf-Workdir), Reviewer kommen mit read-only aus — aber beide nur mit approval_policy=never. Der Schlüssel ist nicht der Sandbox-Modus an sich, sondern das Unterbinden des Approval-Probings, bevor es zur Hang-Schleife wird. Ein read-only-Reviewer, der nur Text liest und ein Verdikt zurückgibt, friert unter approval_policy=never nicht ein — das war der Live-Beweis, nach dem der Hang aufgelöst war.
Was das lehrt: externe CLI-Versionen ändern still ihre Sandbox-Semantik. Was gestern mit einer Konfiguration lief, hängt heute nach einem Update. Nicht dem Wrapper-Output trauen, wenn ein headless Subprozess hängt — das Rollout-Log lesen.
Was offen ist
Die Schleife funktioniert — aber synchron. Builder wechseln sich ab, niemand baut gleichzeitig. Paralleles Bauen mit anschließendem Merge wäre der nächste Schritt: beide Modelle bearbeiten unabhängige Segmente, dann wird zusammengeführt. Das bringt Merge-Komplexität mit sich, die die synchrone Variante vermeidet. Wann der Aufwand sich lohnt, hängt davon ab, wie granular sich eine Aufgabe zerlegen lässt.
Die menschliche Eskalation bei Stagnation ist noch grob. Wenn Reject aufeinander folgt und kein Fortschritt entsteht, gibt es einen einzigen Auslöser: eine Datei, ein Signal. Kein abgestuftes Signal — keine Unterscheidung zwischen „zwei Ablehnungen in Folge beim selben Diff" und „fünf Ablehnungen über mehrere Segmente mit verschiedenen Buildern". Ein abgestufter Signalweg — Warnung, Pause, Eskalation, Abbruch — wäre die nächste Differenzierung, bevor die Schleife autonom über längere Horizonte laufen kann, ohne dass ein Mensch aktiv mitliest.
Wer wen reviewt und nach welcher Logik — daran entscheidet sich, ob ein Relay trägt. Wenn du ähnliche Relay-Muster gebaut hast oder eine andere Aufteilung verwendest, schreib mir.