Ein KI-Code-Review direkt vor git push klingt zunächst nach einer einfachen Sicherheitsmaßnahme: Der Hook prüft die Änderung, und wenn kein Review existiert oder ein schwerwiegender Befund offen ist, darf der Push nicht stattfinden.

Ich habe OpenQodex 0.11.0 in mehreren isolierten, wegwerfbaren Git-Repositories ausprobiert. ShellCheck erkannte tatsächlich einen absichtlich eingebauten Fehler in einem Shell-Skript. Auch der Git-Hook konnte einen Push ohne Review stoppen. Interessant wurde es aber dort, wo ein unabhängiger KI-Reviewer nicht bis zum Ende durchlief: Ein unvollständiges Review muss den Push nicht zwangsläufig blockieren.

Damit ist nichts über die Qualität des eigentlichen KI-Reviews bewiesen. Dieser Teil der Prüfung wurde nicht erfolgreich abgeschlossen. Entscheidend ist vielmehr, dass OpenQodex aus drei getrennten Bausteinen besteht: statischem Scannen, einem unabhängigen Reviewer und einem Push-Gate, das vorhandene Review-Belege (Receipts) nachschlägt. Diese Schritte passieren nicht automatisch als eine einzige Aktion.

ShellCheck fand den Fehler – aber nur, wenn es wirklich ausgeführt wurde

Für den Scanner-Versuch legte ich ein kleines Git-Repository mit zwei Versionen eines Shell-Skripts an. In der Ausgangsfassung stand eine Variable in doppelten Anführungszeichen. In der geänderten Fassung entfernte ich die Anführungszeichen absichtlich, um ShellChecks Regel SC2086 auszulösen.

Getestet wurde OpenQodex 0.11.0 im Scan-Modus, ohne ein Modell als Reviewer zu starten. Die wesentlichen Argumente lauteten:

openqodex scan --uncommitted --only shellcheck --block-on-severity nitpick

Die Zeile ist bewusst vereinfacht. Im tatsächlich ausgeführten Testskript waren außerdem --cwd, --report-dir und der fest angegebene Pfad zum verifizierten Paket enthalten. Sie ist also kein wortgetreues Ausführungsprotokoll. Zum Nachstellen braucht man ein isoliertes Repository mit einem echten Git-Diff und die gleiche CLI-Version. Details stehen in der Dokumentation zum fixierten Commit.

Obwohl der absichtlich fehlerhafte Diff derselbe war, führten zwei Umgebungen zu unterschiedlichen Ergebnissen:

BedingungShellCheck-StatusBefunde im BerichtExit-Code
Offline, Installation verbotennot_installed00
Installation und Ausführung erlaubtran1 (SC2086)1

Die Tabelle enthält bereinigte Beobachtungen aus dem synthetischen Test vom 10. Oktober 2026. Im Online-Durchlauf wurden rawCount=1 und keptCount=1 erfasst, und SC2086 erschien tatsächlich im Bericht. Im Offline-Durchlauf war ShellCheck dagegen gar nicht installiert. Der Scanner konnte deshalb nicht laufen. 0 findings bedeutet hier ausdrücklich nicht „Code geprüft und fehlerfrei“.

Hinzu kommt die Einstufung des Befunds. SC2086 wird in dieser Zuordnung als nitpick behandelt, während review.severity_threshold standardmäßig minor ist. Betrachtet man nur die üblicherweise sichtbaren Ergebnisse, kann man den Eindruck gewinnen, der Scanner habe nichts gefunden. Genau über diesen Filter waren wir zuvor gestolpert. Der Versuch setzte daher bewusst --block-on-severity nitpick, damit der eingefügte Befund in der Ausgabe bleibt.

Seitdem reicht mir ein Exit-Code von 0 nicht mehr. Ich kontrolliere zuerst, ob der Scanner überhaupt lief, und dann, welche ursprünglichen Befunde nach Schweregrad- und Changed-Line-Filterung noch zu sehen sind. Gerade ein fehlendes Werkzeug kann sonst wie ein sauberes Prüfungsergebnis aussehen.

Der echte pre-push-Hook startet nicht automatisch bei jedem Push eine KI

Im nächsten Schritt verwendete ich Git selbst. Als Ziel diente ein lokales bare Repository, nicht das Produktiv-Repository und auch kein gehostetes Remote.

Nach der Installation des tatsächlichen OpenQodex-pre-push-Hooks commitete ich eine nicht geprüfte Änderung. Mit den Standardeinstellungen meldete der Hook das fehlende Review, ließ git push aber durch. Das lokale bare Remote zeigte anschließend auf den neuen Commit.

Danach ergänzte ich die Konfiguration:

version: 1
review:
  block_on_severity: major

Mit einem weiteren, ebenfalls nicht geprüften Commit wurde der Push nun verweigert. Der Test bestätigte zusätzlich, dass main im bare Remote auf demselben Commit wie vor dem Versuch stand. Das war ein real installierter Git-Hook und kein eigens gebautes Skript, das pauschal Exit-Code 1 zurückgibt.

Synthetischer PushBeobachtetes Verhalten
Standardkonfiguration, kein Review-ReceiptWarnung, Push erlaubt
block_on_severity: major, weiterhin kein ReviewPush abgelehnt, Remote-HEAD unverändert

Wichtig ist die Grenze dieses Tests: Bestätigt wurde das Verhalten bei fehlendem Review-Beleg. Es wurde dabei weder Codex als unabhängiger Reviewer gestartet noch ein gültiges Receipt aus einem erfolgreich abgeschlossenen KI-Review erstellt.

Der festgepinnte Hook-Quellcode zeigt, warum. Beim Push prüft OpenQodex den betroffenen Git-Bereich, sucht im OpenQodex HOME des Nutzers einen passenden Review-Beleg und trifft daraufhin eine Gate-Entscheidung. Ein neuer Scan oder ein weiterer Modellprozess wird nicht bei jedem git push automatisch angestoßen.

„scan wurde ausgeführt“, „review wurde vollständig abgeschlossen“ und „hook hat den Push erlaubt“ sind daher drei unterschiedliche Aussagen. Aus einer davon kann man nicht die anderen beiden ableiten.

Warum kann der Hook trotz eines gescheiterten Review-Versuchs freigeben?

Der dritte Versuch betraf den unabhängigen Codex Reviewer auf einem Mac. Der zweite Prozess startete eine Anfrage, erhielt jedoch HTTP 401. OpenQodex vermerkte das Ergebnis als incomplete; es entstand kein gültiger Beleg für ein vollständig abgeschlossenes Review. Über Bug-Erkennung, False Positives oder den kompletten Ablauf gültiger und veralteter Receipts kann dieser Versuch also nichts aussagen.

In der Zusammenfassung des Mac-Tests stand außerdem eine auffällige Beobachtung: Ein erster, nicht geprüfter Push wurde abgelehnt; nach dem unvollständigen Review kam ein weiterer Push mit Exit-Code 0 durch und legte einen Remote-Branch an. Allerdings waren die Ziel-Refs dieser beiden Pushes nicht exakt gleich. Auch die originalen Mac-Berichte liegen noch nicht als unabhängig geprüfte Evidenz im Dossier vor. Deshalb wäre es unredlich, aus dieser Abfolge bereits eine vollständig reproduzierte Ursache-Wirkung-Kette abzuleiten.

Was sich unabhängig davon prüfen lässt, ist die konkrete Implementierung von OpenQodex 0.11.0.

In der fixierten Datei push-gate.ts gibt checkPush() bei einem zur aktuellen Änderung passenden Receipt vom Typ incomplete die Entscheidung abstain zurück. Es blockiert also nicht aktiv. Selbst mit review.block_on_severity: major wird dieser Zweig nicht zur Ablehnung.

Daneben gibt es die Logik des generierten Git-Hooks in hook.ts: Nur wenn der aufgerufene Unterbefehl mit 1 endet, verlässt auch der Hook das Skript mit 1 und verhindert den Push. Andere Werkzeugfehler oder ein nicht startender Prozess werden nach außen zu Exit-Code 0. Damit kann ein defekter Review-Dienst die Arbeit mit Git nicht vollständig lahmlegen. Die Kehrseite: Ein Schweregrad-Limit erzwingt kein vollständig erfolgreiches Review.

Das ist zunächst eine Entscheidung über das Ausfallverhalten und kein Nachweis einer beliebigen Sicherheitslücke in produktiven Repositories. Ein Werkzeug kann bei Störungen bewusst die Entwicklung weiterlaufen lassen. Ein Team kann genau das Gegenteil verlangen: Scheitert das Review, muss die Änderung gestoppt werden. Problematisch wird es, wenn man die eine Regel für die andere hält.

Wie ich ein tatsächlich verpflichtendes Review absichern würde

Ich würde OpenQodex vorerst als Entwickler-Feedback einsetzen, nicht als einziges, nicht umgehbares Merge-Gate. Der Scanner liefert konkrete Regeln und Dateipositionen. Der Hook erinnert an ein fehlendes Review. Ob ein Commit gemergt werden darf, würde ich dagegen durch einen separaten, nachvollziehbaren CI-Check entscheiden lassen, der mindestens folgende Fragen prüft:

  • Wurden alle notwendigen Scanner tatsächlich ausgeführt, oder steht irgendwo not_installed, übersprungen oder ein Fehler?
  • Ist der unabhängige KI-Reviewer wirklich fertig, statt unavailable oder incomplete zu melden?
  • Gehört der Beleg zum aktuellen Commit beziehungsweise Diff und zur momentan geltenden Policy?
  • Erzwingt die Branch Protection den erfolgreichen required check auch dann, wenn CI oder der Review-Dienst ausfällt?

Das ist eine Architektur-Empfehlung, kein in diesem Experiment erfolgreich erprobtes GitHub-Branch-Protection-Setup. Wer jedes Merge an ein vollständiges KI-Review binden will, muss das Fail-closed-Verhalten seiner CI-Kette gesondert nachweisen. Die lokale pre-push-Konfiguration allein liefert diesen Beweis nicht.

Bei einem privaten Entwicklerprojekt würde ich nicht unbedingt vor jedem Push noch einen Modellaufruf auslösen. Bei Bedarf review starten, statische Scanner als schnelle Rückmeldung nutzen und nur wichtige Branches durch einen wirklich getesteten CI-Check absichern – das kann Kosten und Arbeitsfluss besser ausbalancieren. Entscheidend bleibt, „Hook installiert“ nicht mit „unabhängige KI hat diesen Code geprüft“ gleichzusetzen.

Der nachgewiesene Umfang ist überschaubar: OpenQodex 0.11.0 erkannte SC2086 in einem synthetischen Shell-Diff; ein lokales bare Remote zeigte den Unterschied zwischen bloßer Warnung und konfigurierter Ablehnung eines ungeprüften Pushes. Die fixierte Implementierung enthält außerdem explizite Wege, auf denen incomplete-Receipts und manche Hook-Fehler einen Push nicht blockieren. Ein vollständiges unabhängiges Modell-Review wurde nicht erfolgreich durchgeführt. Belastbare Aussagen über Modellgenauigkeit, Fehlalarme, Latenz oder einen gültigen Receipt-Roundtrip wären erfunden.

Solange dieser letzte Punkt offen ist, setze ich den lokalen OpenQodex-Hook nicht als einzige verpflichtende KI-Review-Schranke für produktiven Code ein. Als Scan- und Review-Einstieg bleibt er nützlich. Bevor ich ihm die Entscheidung über einen Push überlasse, möchte ich aber wissen, wer bei einem fehlgeschlagenen Reviewer tatsächlich stoppt.