Zum Inhalt springen

Review-Gate für KI-generierten Code

Sieht im Review gut aus. Bricht in Produktion.

Unslop prüft jeden Pull Request auf die Art, wie KI-geschriebener Code scheitert: verschluckte Fehler, Pakete, die es nicht gibt, SQL aus zusammengesetzten Strings, Tests ohne Assertion.

Zuerst läuft ein deterministischer Pre-Scan. Dann prüft ein Modell den Diff, und ein zweites Modell prüft dessen Findings gegen, ohne die Begründung des ersten zu sehen. Jedes Finding nennt seine Regel und zitiert den Code.

So entsteht das Urteil

Private Beta. Die Registrierung ist geschlossen, und CLI, Editor-Extension und MCP-Server sind noch nicht veröffentlicht.

Das Finding zeigen. Das Urteil erklären. Sagen, was übersprungen wurde.

unslopai/test · pull request #20

src/lib/user-lookup.ts

1+import type { Pool } from 'pg';
2+
3+/** Loads a user row by id. */
4+export async function loadUserById(pool: Pool, userId: string) {
5+ return pool.query(`SELECT id, email FROM users WHERE id = ${userId}`);
6+}
unslop-gatekeepersrc/lib/user-lookup.ts:5

SEC-004 (SQL built by string interpolation) (CRITICAL)

The SQL statement is assembled from interpolated/concatenated values — SQL injection. Use parameterized queries exclusively.

Anti-Slop Gatekeeper · 16 sfailure · 1 critical slop finding
Echte Ausgabe, kein Mock-up: Check Run und Review-Kommentar an Pull Request #20 in unserem eigenen Test-Repository, 30.09.2026. Dieses Finding stammt aus dem deterministischen Pre-Scan.

Zeigen statt behaupten

Zwei weitere Findings, so wie sie gespeichert sind

Beide stammen aus Scans unseres eigenen Test-Repositorys. Regel, zitierter Code, Erklärung und Fix sind Produktausgabe, unbearbeitet. Das Produkt schreibt seine Findings auf Englisch.

Modell-Finding, von der blinden Gegenprüfung bestätigt

Condition 1 (Silent Error Swallowing)

CRITICALconfirmedfix-probe.ts:4

Zitierter Code

    } catch (e) {
        console.error(e);

Vorgeschlagener Fix

    } catch (e) {
        throw new Error(`Failed to parse configuration: ${e instanceof Error ? e.message : String(e)}`);
    }

The catch block performs passive logging and returns null instead of propagating the error or triggering a compensating mechanism, silently swallowing parsing failures.

CLI-Scan vom 17.09.2026, Job 0cf82b14. Das prüfende Modell hat das Finding entworfen. Das zweite Modell hat es allein aus Regel und zitiertem Code bestätigt.

Modell-Finding, herabgestuft, weil eine Stufe übersprungen wurde

MAINT-007

WARNINGunverifiedsrc/lib/prescan/companion-loader.ts:138

Zitierter Code

                if (listError instanceof GitHubApiError && listError.status === 404) return null;

Vorgeschlagener Fix

                if (readError instanceof GitHubApiError && readError.status === 404) return null;

The catch block catches 'readError', but the condition references 'listError', which is out of scope. This causes a TypeScript compile error (TS2304: Cannot find name 'listError') and a runtime ReferenceError whenever fetchFileContent throws an error.

⚠️ Downgraded to WARNING — this finding was flagged for Pro escalation, but the escalation was skipped (budget, API or time-limit degradation), so it could not be conclusively verified.

Pull Request #17, 26.09.2026, Job f2c676ba. Für diesen Lauf haben wir das Zeitbudget absichtlich auf 150 Sekunden gekürzt, um diesen Pfad zu testen. Der dritte Durchgang lief nicht, also wurde das Finding zur Warnung herabgestuft, und der Kommentar sagt, warum.

01

Erst deterministisch

65 Regeln prüft Code, kein Modell.

Der Pre-Scan läuft vor jedem Modell-Aufruf. Er nutzt Pattern-Matcher, Syntaxbaum-Abfragen, Config-Checks, Lint-Regeln und Abfragen der Paket-Registry und hat einen Detektor für 65 Regel-IDs.

Ein deterministisches Finding wird nie von einem Modell neu beurteilt. Meldet das Modell dieselbe Regel an derselben Stelle, gewinnt das deterministische Finding. Im Ergebnis ist es als deterministic markiert.

02

Modell-Review, blind gegengeprüft

Ein zweites Modell prüft jedes Finding gegen, ohne die Begründung des ersten.

Der Regelkatalog hat 127 Einträge. Regeln ohne Detektor brauchen Urteilsvermögen, also prüft ein Modell den Diff gegen die Regeln, die zu deinem Stack passen. Manche Regeln beschreiben Prozesse und lassen sich an einem Diff gar nicht prüfen. Was das Modell meldet, ist eine Behauptung, noch kein Finding.

Ein zweites Modell bekommt nur die Regel, die Fundstelle, den zitierten Code und den Diff. Erklärung, Schweregrad und Fix des ersten Modells sieht es nicht. Es antwortet confirmed, refuted oder uncertain. Widerlegte Behauptungen fallen weg. Unsichere sowie kritische, die widerlegt oder nur schwach bestätigt wurden, bekommen einen dritten Durchgang. Was unsicher bleibt, wird zur Warnung und sagt das auch.

Zwei Dinge solltest du wissen. Alle Modelle sind Gemini-Modelle eines Anbieters: Die Unabhängigkeit kommt daher, was das zweite Modell nicht zu sehen bekommt, nicht aus einer anderen Modellfamilie. Und nicht jedes Finding bekommt die blinde Gegenprüfung: Große Diffs können in einem einzelnen Durchgang ohne sie geprüft werden, und das Review sagt das dann.

03

Wenn eine Stufe ausfällt

Eine übersprungene Stufe wird ausgewiesen, nicht versteckt.

Jedes Review hat ein Zeitbudget von 300 Sekunden. Wenn die Gegenprüfung oder der dritte Durchgang wegen Zeit, eines Modellfehlers oder eines aufgebrauchten Budgets nicht laufen kann, gelten die Findings nicht stillschweigend als verifiziert. Ein Finding, das auf den dritten Durchgang gewartet hat, wird mit einem Hinweis zur Warnung herabgestuft. Lief die Gegenprüfung nicht, steht beim Konfidenz-Score „n/a“ statt einer Zahl.

Pull Request, Check Run und CLI tragen dann dieselbe Zeile, zum Beispiel: „Reduced confidence: time budget exhausted — skipped: blind verification (some or all findings).“ Scheitert das Modell-Review an einzelnen Dateien, werden diese Dateien namentlich genannt.

Wer entscheidet

Welcher Teil des Urteils woher kommt

Welcher Teil des Urteils woher kommt
FrageDeterministischer Pre-ScanModell-Review
Regeln65 Regel-IDs mit DetektorRegeln, die Urteilsvermögen brauchen, aus einem Katalog von 127
So entsteht ein FindingCode: Patterns, Syntaxbaum, Config-Checks, Lint-Regeln, Registry-AbfragenEin Modell entwirft es, ein zweites prüft es blind gegen
DateienTypeScript, JavaScript, Python, Java, Go, Rust, C und C++, dazu Config-Dateien wie YAML, JSON, Terraform und TOMLNur TypeScript, JavaScript und Python
Kann es den Check scheitern lassen?Ja. Ein CRITICAL-Finding lässt den Check scheitern.Ja. Ein CRITICAL-Finding lässt den Check scheitern, ein Urteil kann also auf einem Modell-Finding beruhen.
Im Ergebnis markiert alsdeterministicconfirmed, uncertain oder unverified

Das Problem

Was durchs Review rutscht

„Es lief ohne Fehler.“

Weil der Fehler gefangen und verworfen wurde. Ein catch-Block, der loggt und null zurückgibt, sieht im Diff ordentlich aus und versteckt den Ausfall in Produktion. Das bestätigte Finding oben ist genau das.

„Der Import sah plausibel aus.“

Der Paketname war erfunden. Unslop schlägt die in package.json, requirements.txt, pyproject.toml und Cargo.toml deklarierten Abhängigkeiten in ihrer Registry nach (Regel SEC-035).

„Die Tests sind grün.“

Ein Test ohne Assertion ist immer grün. Tests ohne Assertion sind eine der deterministischen Regeln (TEST-001).

„Ich habe nicht alle vierzig Dateien gelesen.“

Der Agent hat mehr geändert, als irgendjemand gelesen hat. Unslop prüft jeden Push auf den Pull Request erneut und kommentiert an der betroffenen Zeile.

Plattform

Ein Review, vier Oberflächen

Pull Request, Terminal, Editor, Coding-Agent.

Regelkatalog
127 Regeln, jede mit einer ID. 65 haben einen deterministischen Detektor. Das Modell-Review bekommt die Regeln, die zu deinem Stack passen; manche Regeln beschreiben Prozesse und lassen sich an einem Diff nicht prüfen.
PR-Gate
GitHub App. Jedes Review ist ein Check Run namens „Anti-Slop Gatekeeper“ plus Kommentare an den Zeilen. Ein CRITICAL-Finding lässt den Check scheitern; Warnungen enden als neutral.
Repository-Kontext
Bei verbundenen Repositorys werden Signaturen und Typdefinitionen aus deinem Code als Kontext für das Modell abgerufen. Sie informieren das Review; einen Regelverstoß entschuldigen sie nie.
Jeder Push
Jeder Push auf einen Pull Request wird für sich geprüft. Einen Vergleich mit der vorigen Revision gibt es nicht.
CLI, Editor, MCP
„unslop scan“ prüft deine lokalen Änderungen gegen die Merge-Base und kann vorgeschlagene Fixes anwenden. Die VS-Code-Extension zeigt Findings als Diagnostics. Ein MCP-Server bietet den Scan für Coding-Agents an. Alle drei sind gebaut und noch nicht veröffentlicht.
Webhook-Sicherheit
Jeder GitHub-Webhook wird über seine HMAC-SHA256-Signatur geprüft.
Verschlüsselung
GitHub-OAuth-Tokens liegen mit AES-256-GCM verschlüsselt in der Datenbank. API-Keys werden als Hash gespeichert.
Geplant (Konzept)
Noch nicht gebaut: den Check scheitern lassen, wenn eine Revision mehr Findings bringt als die vorige, und ein zweiter Modellanbieter für die Gegenprüfung.
Ehrliche Grenzen
Nichts findet jeden Fehler in KI-geschriebenem Code, auch wir nicht. Das Modell-Review liest nur TypeScript, JavaScript und Python, und seine Ergebnisse schwanken zwischen Läufen.

FAQ

Häufige Fragen

Speichert ihr unseren Quellcode oder trainiert ihr damit?

Wir trainieren nicht damit, und Google auch nicht: Die Service-Bedingungen von Vertex AI schließen aus, dass Google Prompts und Antworten von Kunden zum Training eigener Modelle nutzt. Die Review-Modelle laufen auf dem EU-Multiregion-Endpunkt von Vertex AI, für den Google zusagt, dass die ML-Verarbeitung innerhalb der EU-Mitgliedstaaten bleibt. Für einen Pull Request lesen wir den Diff, die geänderten Dateien und eine kleine, regelbasierte Auswahl weiterer Dateien des Repositorys (Manifeste, Datenbankschema, die Startdatei eines Dienstes) von GitHub für das Review und speichern die Findings mit den zitierten Zeilen, nicht den Diff und nicht diese Dateien. Ein Scan aus der CLI oder dem MCP-Server lädt einen Diff und dieselbe Auswahl von Dateien hoch; der Diff wird mit dem Scan gespeichert, die Auswahl nur, bis das Review sie geladen hat; vor dem Upload verweigert der Client jeden Diff, in dem er ein Secret erkennt. Der Repository-Index entsteht aus Signaturen, Interfaces und Typdefinitionen, nicht aus Implementierungslogik. GitHub-OAuth-Tokens liegen mit AES-256-GCM verschlüsselt in der Datenbank.

Bremst das unsere CI/CD oder blockiert unsere Merges?

Es läuft neben deiner CI, nicht in ihr. Der Webhook antwortet, sobald das Review registriert ist; das Review läuft dann als GitHub Check Run mit einer harten Grenze von 300 Sekunden. Nur CRITICAL-Findings lassen den Check scheitern; Warnungen enden als neutral und blockieren keinen Merge. Ob der Check verpflichtend ist, legst du in deiner Branch Protection fest.

Muss mein Team noch ein Dashboard lernen?

Nein. Findings erscheinen als Check Run und als Review-Kommentare am Pull Request. Die CLI vergleicht deine getrackten Änderungen mit der Merge-Base, und die VS-Code-Extension zeigt Findings und wendet vorgeschlagene Fixes im Editor an; beide sind gebaut und noch nicht veröffentlicht. Das Dashboard ist für Repositorys, Scan-Historie, API-Keys und Abrechnung da.

Ist das nur ein weiterer KI-Reviewer?

Zum Teil, und wir sagen, welcher Teil. 65 Regeln prüfen deterministische Detektoren, und diese Findings laufen nie durch ein Modell. Der Rest ist ein Modell-Review, und ein Finding daraus kann den Check scheitern lassen. Deshalb prüft ein zweites Modell die Findings gegen, ohne die Begründung des ersten zu sehen, und deshalb sagt das Review, wenn diese Gegenprüfung nicht stattgefunden hat. Modell-Aufrufe laufen mit Temperature 0; Ergebnisse schwanken trotzdem etwas zwischen Läufen.

Welche Sprachen werden abgedeckt?

Das Modell-Review liest TypeScript-, JavaScript- und Python-Dateien. Der deterministische Pre-Scan deckt zusätzlich Java, Go, Rust, C und C++ sowie gängige Config-Dateien ab. Ein Pull Request ohne TypeScript-, JavaScript- oder Python-Datei bekommt nur deterministische Prüfungen, wenn er Java-, Go-, C-, C++-, PowerShell-, Terraform-, YAML- oder JSON-Dateien ändert. Dann liest kein Modell mit: Der Check heißt „Deterministic checks only“ und bleibt neutral, solange kein kritisches Finding ihn scheitern lässt. Mischt ein Pull Request beides, nennt die Zusammenfassung die Dateitypen, die kein Modell gelesen hat. Alles andere, etwa ein reiner Doku-Pull-Request, endet weiter als neutral mit „Nothing to review“.

Was passiert, wenn ein Review scheitert?

Der Check wird geschlossen und bleibt nicht hängen. Ein gescheitertes Review endet als Failure mit dem Grund im Check, zum Beispiel „Review timed out“. Ein Review, das gar nicht startet (kein Abo, Kontingent aufgebraucht, Repository deaktiviert), postet einen abgeschlossenen Check mit dem Ergebnis „action required“, der den Grund nennt. Ist der Check bei dir verpflichtend, gelten weiter die Branch-Protection-Overrides von GitHub.

Das Finding zeigen. Das Urteil erklären. Sagen, was übersprungen wurde.

Unslop ist in der Private Beta. Die Registrierung ist vorerst geschlossen.