A push gate that quizzes you on your AI's code ships its own unresolved false negatives
npm install -g sphinx-understand , then git push . Git stops on the branches you protected. A model that never saw your chat session reads the diff and asks you one question about what the change actually does. Get it wrong and it explains why, then asks a different scenario built from the same con

npm install -g sphinx-understand, dann git push. Git stoppt an den geschützten Branches. Ein Modell, das deine Chat-Sitzung nie gesehen hat, liest den Diff und stellt dir eine Frage dazu, was die Änderung tatsächlich bewirkt. Beantwortest du sie falsch, erklärt es dir warum und stellt eine andere Frage zu einem Szenario, das auf demselben Konzept aufbaut. Beantwortest du sie richtig, läuft der Push weiter. sphinx yolo lässt den Push durch und protokolliert das Überspringen.
Das ist Sphinx, MIT-lizenziert, 117 Sterne, ein Git-Hook für agentenunterstützte Entwicklung. Der Claim auf der Website ist ein Zitat von Sun Tzu: Dein Denken kannst du auslagern, dein Verständnis nicht. Der Show-HN-Thread erreichte 242 Punkte und 85 Kommentare und spaltete sich fast gleichmäßig zwischen der Frage, für wen das gedacht ist, und Leuten, die dasselbe gebaut haben. In diesem Thread gibt es ein zweites Projekt, das eine Version auf Pull-Request-Ebene umsetzt.
Zu dieser Debatte habe ich nicht viel hinzuzufügen. Was mich interessiert, ist, dass das Repository ein eval/-Verzeichnis mitliefert, und dieses eval-Verzeichnis ist ein besseres Artefakt als das Produkt selbst.
Ein Verständnis-Gate ist eine Testsuite, bei der das getestete Objekt eine menschliche Erklärung in Freitext ist. Der Bewerter ist ein Modell. Man hat also LLM-as-judge gebaut, auf Prosa gerichtet und das Ergebnis mit einem harten Stopp beim Push verbunden. Das ist ein Design mit einem sehr spezifischen Fehlermodus, und die meisten Projekte, die so etwas ausliefern, schreiben nie auf, welcher das ist.
Sphinx hat es aufgeschrieben. eval/RESULTS.md beendet einen Live-Lauf mit diesem Satz: "The result is not an all-green semantic evaluation, despite all 560 guarded unit/integration tests passing."
560 grüne Tests, und der Autor sagt einem, dass die semantische Evaluierung nicht grün ist. Diese Unterscheidung ist der Kern des Ganzen. Unit-Tests verifizieren, dass die Pipeline Bytes korrekt bewegt. Sie sagen nichts darüber aus, ob der Bewerter deine Antwort akzeptiert hätte.
Das Repository führt eine Datei namens eval/fixtures/known-false-negatives.json. Ihr scope-Feld lautet: "Synthetic fixture questions and authored answers only. These are known failures, not passing model assertions." Die eval-README nennt sie "unresolved regressions, not passing tests".
Beide Einträge enthalten eine menschliche Antwort, das Urteil, das das Modell gefällt hat, das Feedback des Modells und einen unabhängigen Befund, dass das Urteil falsch war.
Fall eins ist eine CSS-Frage. Der Diff fügt eine Regel a.card.button hinzu, die nur padding-block setzt, auf einer Basisregel base.button aufbauend. Die Frage verlangt, vorherzusagen, welche Achse sich bei einem Card-Button und bei einem Toolbar-Button ändert, und warum. Die verfasste Antwort sagt, der Toolbar-Button sei nicht betroffen, weil der neue Selektor einen Card-Vorfahren benötigt, und dass das horizontale Padding unverändert bleibe, weil die Basisregel es weiterhin liefert.
Der Bewerter markierte sie als mehrdeutig, mit diesem Feedback: "It never states that the vertical (block-direction) padding is what changes for Complete task, so the required per-axis prediction is missing."
Lies das noch einmal gegen die Antwort. Die Antwort beschrieb das neue Card-Padding plus das unveränderte horizontale Padding, womit genau eine Achse übrig bleibt. Sie nannte den Vorfahren-Scope. Sie nannte die unveränderte Toolbar-Grenze. Die Bestehensbedingung des Bewerters war zu einer Vokabelprüfung auf das Wort "vertical" zerfallen.
Das ist auch kein Ausrutscher einer einzigen billigen Route. Ein späterer Lauf spielte dieselbe Frage und Antwort gegen ein stärkeres Modell und reproduzierte das falsche Negative in einer Anfrage, in 4,6 Sekunden. Der Bericht weist darauf hin, dass die Abbruchbedingung den Vergleich an dieser Stelle beendete, sodass es keine Behauptung gibt, das stärkere Modell falle in der breiteren Matrix durch.
Fall zwei ist Autorisierung. Der Patch verschmilzt eine Missing-Document-Prüfung und eine Cross-Tenant-Prüfung zu einer Bedingung, die dasselbe { status: 404 } zurückgibt. Die verfasste Antwort sagt, ein anderer Tenant erhalte einen 404 ohne Body, "genau wie bei einer fehlenden id". Der Bewerter wollte ein explizites "nein" und den wörtlichen Mechanismus der zusammengeführten Bedingung.
Der Teil, der stören sollte: Der Bericht sagt, dass die quellengeprüften Akzeptanzkriterien selbst unnötigerweise eine einzige kombinierte Bedingung nannten. Die Prüfung, die das Bewerten fairer machen sollte, hatte die Vokabelanforderung in den Schlüssel eingebrannt. Der Autor stellt ausdrücklich klar, dass sowohl die Frage-Schlüssel als auch die akzeptierten Ersetzungen quellkorrekt waren und dass Quellkorrektheit die ausreichende Antwort nicht fair bewertet hat.
Hier ist der Teil, der mehr wert ist als das Tool. Die eval-README dokumentiert, wie man ein bekanntes falsches Negative erneut abspielt:
Um eines wiederzuspielen, wähle seine source.caseId aus CASES, lege die gespeicherte source.definition darüber und rufe createFixture(fixture, config) auf. Lies die unveränderlichen Beweise mit createReviewWorkspace(snapshot, config, { env }) und canonicalEvidence(snapshot, record.source.requiredPaths, workspace), erstelle dann ein Review, das an diese neue snapshot.id gebunden ist, und rufe evaluator.gradeAnswer auf. Nutze den vorhandenen metered evaluator transport und ein explizites Live-Budget.
Dann die Einschränkung, die daraus einen echten Test statt einer Demo macht: "do not reuse an old temporary snapshot ID, re-audit to an easier question, or replace the saved human answer."
Das ist der Unterschied zwischen einem Benchmark und einer Marketingseite. Die gespeicherte Antwort ist eingefroren. Man kann den Bewerter, die Kriterien, den Prompt, die Modellroute ändern und gegen die identische Eingabe erneut laufen lassen. Wenn man die Frage so lange umschreibt, bis sie besteht, hat man den Test verändert, und die Datei sagt genau das laut aus.
Auf der anderen Seite gibt es eine passende Absicherung. npm test und npm run check laden einen Netzwerk-Guard in Node und seine Kindprozesse, der ausgehende fetch-, HTTP- und Socket-Aufrufe ablehnt und Loopback erlaubt. Der angegebene Grund: Ein partielles Modell-Mock soll nicht in der Lage sein, stillschweigend einen echten Anbieter zu erreichen. Für ein Projekt, dessen ganze Aufgabe darin besteht, deterministische Aussagen über einen nichtdeterministischen Bewerter zu machen, ist das der richtige Instinkt.
Der Konfigurationsfall ist die andere Lektion. Das Fixture fragte nach einer BATCH_SIZE-Präzedenzregel mit der Formulierung "BATCH_SIZE is set to 5". Der unabhängig verfasste Probe-Adapter erkannte nur BATCH_SIZE=number und beantwortete deshalb ein anderes Szenario mit einem anderen Wert.
Die Schlussfolgerung des Berichts ist der nützliche Teil: "That is an uncovered scenario, so this result does not establish how the model would grade a sufficient answer to the stated input."
Ein rotes Ergebnis von einem Fixture, das die angegebene Eingabe nie vorgelegt hat, sagt nichts über den Bewerter aus. Es sagt etwas über das Fixture aus. Wenn dein Evaluierungsbericht diese beiden Fälle nicht unterscheidet, ist jede rote Zahl Rauschen, dem man eine Woche hinterherjagt.
Derselbe Lauf erzeugte auch eine Rohantwort, die die Vorhersage als mehrdeutig bezeichnete, kein wesentliches Missverständnis einräumte und dann incorrect ohne required transfer auswarf. Die Validierung wies die fehlerhafte Bewertung zurück. Das ist wieder eine andere Fehlerklasse: ein Bug im Antwortformat in den Kleidern eines semantischen Fehlers.
Nach der Wiederholung des falschen Negativen sagt der Bericht, das mehrdeutige Ergebnis "leaves the checkpoint pending and grants no approval; safe noncompletion is separate from fair grading."
Das sind zwei unterschiedliche Eigenschaften, und sie werden ständig vermischt. Ein Gate, das beim Versagen des Bewerters nach dem Fail-closed-Prinzip blockiert, ist sicher. Es ist nicht korrekt, und es ist nicht fair gegenüber der Person, die eine gute Antwort geschrieben hat. Teams, die ein Verständnis-Gate oder ein KI-Review-Gate einführen, prüfen meist nur die erste Eigenschaft.
Dieselbe Frage stellt man generell an KI-Code-Review-Tools. Wenn man Optionen vergleicht, sollte man fragen, was das Tool über die Läufe veröffentlicht, die es falsch gemacht hat. Ein Changelog mit Fixes ist nicht dasselbe Artefakt wie eine eingefrorene Liste von Antworten, die weiterhin falsch bewertet werden. Manche Projekte veröffentlichen das Zweite, manche nicht, und diejenigen, die es tun, kann man eher mit einem blockierenden Check in der Pipeline vertrauen. Das ist die erste Frage, die ich an Kodus oder jeden anderen Reviewer stellen würde, den man in CI einbinden will.
Das Repository hat außerdem seine Assessment-Policy auf Contract-Version 3 angehoben, sodass Fragen und Belege, die unter dem früheren Contract erstellt wurden, nicht stillschweigend wiederverwendet werden. Wenn man Prompts versioniert, sollte man auch die von ihnen erzeugten Artefakte versionieren, sonst autorisiert ein veralteter Beleg einen Push unter Regeln, die nicht mehr existieren.
Vier Dinge, und sie entsprechen den Fehlerklassen oben.
Ob das Projekt eine Datei mit den Dingen führt, die es derzeit falsch macht. Eine Liste der Erfolge ist leicht. Eine eingefrorene Liste ungelöster Fehler ist das Artefakt, das zeigt, dass die Autoren messen.
Ob man eine gespeicherte Eingabe durch einen veränderten Bewerter mit explizitem Kostenbudget erneut laufen lassen kann. Wenn die Wiederholung einer festgelegten Antwort das Neuschreiben der Frage erfordert, kann man keinen Fix messen.
Ob eine nicht abgedeckte Eingabeformulierung als nicht abgedeckt statt als fehlgeschlagen gemeldet wird. Sonst füllt sich das Dashboard mit Fehlern, die nie stattgefunden haben.
Ob "der Push wurde blockiert" als Erfolgsmetrik für sich gemeldet wird. Blockieren lässt sich leicht erreichen, indem man schlecht bewertet. Die entscheidende Metrik ist, wie viele ausreichende Antworten akzeptiert wurden.
Nichts davon spricht gegen das Tool. Es spricht dafür, dass das eval-Verzeichnis der Grund ist, sich das Repository anzusehen, und dass ein Gate, das dein Verständnis bewertet, zuerst sein eigenes Verständnis geprüft bekommen muss. Die Autoren haben die beiden Fälle veröffentlicht, in denen es theirs nicht war.
Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.
Original (Englisch) anzeigen
npm install -g sphinx-understand, then git push. Git stops on the branches you protected. A model that never saw your chat session reads the diff and asks you one question about what the change actually does. Get it wrong and it explains why, then asks a different scenario built from the same concept. Get it right and the push continues. sphinx yolo lets the push through and logs the skip.
That's Sphinx, MIT licensed, 117 stars, a Git hook for agent-assisted development. The pitch on its site is a Sun Tzu quote: you can outsource your thinking, but not your understanding. The Show HN thread hit 242 points and 85 comments and split almost evenly between "who is this for" and people who built the same thing. There's a second project in that thread doing a PR-level version.
I don't have much to add to that argument. What I care about is that the repo ships an eval/ directory, and the eval directory is a better artifact than the product.
A comprehension gate is a test suite where the thing under test is a human explanation written in free text. The grader is a model. So you've built LLM-as-judge, pointed it at prose, and wired the result to a hard stop on your push. That's a design with a very specific failure mode, and most projects shipping one never write down what it is.
Sphinx wrote it down. eval/RESULTS.md ends a live run with this line: "The result is not an all-green semantic evaluation, despite all 560 guarded unit/integration tests passing."
560 green tests, and the author is telling you the semantic evaluation is not green. That distinction is the whole piece. Unit tests verify the pipeline moves bytes around correctly. They say nothing about whether the grader would have accepted your answer.
The repo keeps a file called eval/fixtures/known-false-negatives.json. Its scope field reads: "Synthetic fixture questions and authored answers only. These are known failures, not passing model assertions." The eval README calls them "unresolved regressions, not passing tests."
Both entries are a human answer, the verdict the model gave it, the model's feedback, and an independent finding that the verdict was wrong.
Case one is a CSS question. The diff adds a.card.button rule that sets only padding-block, on top of a base.button rule. The question asks you to predict per axis what changes for a card button and for a toolbar button, and why. The authored answer says the toolbar button is unaffected because the new selector needs a card ancestor, and that horizontal padding is unchanged because the base rule still supplies it.
The grader marked it ambiguous, with this feedback: "It never states that the vertical (block-direction) padding is what changes for Complete task, so the required per-axis prediction is missing."
Read that again against the answer. The answer described new card padding plus unchanged horizontal padding, which leaves exactly one axis. It named the ancestor scope. It named the unchanged toolbar boundary. The grader's pass condition had collapsed into a vocabulary check for the literal word "vertical."
That isn't a fluke of one cheap route either. A later run replayed the same question and answer against a stronger model, and it reproduced the false negative in one request, 4.6 seconds. The writeup notes the stop condition ended the comparison there, so there's no claim the stronger model fails the broader matrix.
Case two is authorization. The patch merges a missing-document check and a cross-tenant check into one condition returning the same { status: 404 }. The authored answer says a different tenant gets a bodyless 404 "just like a missing id." The grader wanted an explicit "no" and the literal combined-condition mechanism.
The part that should bother you: the writeup says the source-audited acceptance criteria themselves unnecessarily named a single combined condition. The audit that was supposed to make grading fairer had baked the vocabulary requirement into the key. The author is explicit that both question keys and the accepted replacements were source-correct, and that source correctness did not make the sufficient answer fairly graded.
Here's the part I think is worth more than the tool. The eval README documents how to replay a known false negative:
To replay one, select its source.caseId from CASES, overlay the saved source.definition, and call createFixture(fixture, config). Read immutable evidence with createReviewWorkspace(snapshot, config, { env }) and canonicalEvidence(snapshot, record.source.requiredPaths, workspace), then build a review bound to that new snapshot.id and call evaluator.gradeAnswer. Use the existing metered evaluator transport and an explicit live budget.
Then the constraint that makes it a real test instead of a demo: "do not reuse an old temporary snapshot ID, re-audit to an easier question, or replace the saved human answer."
That's the difference between a benchmark and a marketing page. The saved answer is frozen. You can change the grader, the criteria, the prompt, the model route, and re-run against the identical input. If you rewrite the question until it passes, you've changed the test, and the file says so out loud.
There's a matching guard on the other side. npm test and npm run check preload a network guard into Node and its spawned children, rejecting outbound fetch, HTTP, and socket calls while allowing loopback. The stated reason is that a partial model mock shouldn't be able to silently reach a real provider. For a project whose whole job is making deterministic claims about a nondeterministic grader, that's the right instinct.
The configuration case is the other lesson. The fixture asked about a BATCH_SIZE precedence rule using the phrasing "BATCH_SIZE is set to 5." The independently authored probe adapter only recognized BATCH_SIZE=number, so it answered a different scenario with a different value.
The writeup's conclusion is the useful bit: "That is an uncovered scenario, so this result does not establish how the model would grade a sufficient answer to the stated input."
A red result from a fixture that never presented the stated input tells you nothing about the grader. It tells you about the fixture. If your eval report doesn't distinguish those two, every red number is noise you'll spend a week chasing.
The same run also produced a raw response that called the prediction ambiguous, acknowledged no material misconception, and then emitted incorrect with no required transfer. Validation rejected the malformed grade. That's a separate failure class again, a response-shape bug wearing a semantic failure's clothes.
After the false-negative replay, the writeup says the ambiguous result "leaves the checkpoint pending and grants no approval; safe noncompletion is separate from fair grading."
Those are two different properties and they get conflated constantly. A gate that fails closed when the grader is confused is safe. It is not correct, and it is not fair to the person who wrote a good answer. Teams adopting any comprehension gate, or any AI review gate, tend to only verify the first one.
The same question applies to AI code review tools generally. When you're comparing options, ask what the tool publishes about the runs it got wrong. A changelog of fixes is not the same artifact as a frozen list of answers that are still being graded incorrectly. Some projects publish the second thing and some don't, and the ones that do are easier to trust with a blocking check in your pipeline. It's the first question I'd put to Kodus or any other reviewer you're considering wiring into CI.
The repo also bumped its assessment policy to contract version 3 so questions and receipts prepared under the earlier contract are not silently reused. If you version your prompts, version the artifacts they produced too, or a stale receipt will authorize a push under rules that no longer exist.
Four things, and they map to the failure classes above.
Whether the project keeps a file of things it currently gets wrong. A pass list is easy. A frozen list of unresolved failures is the artifact that tells you the authors are measuring.
Whether you can re-run a saved input through a changed grader with an explicit cost budget. If replaying a fixed answer requires re-authoring the question, you can't measure a fix.
Whether an uncovered input phrasing is reported as uncovered instead of failed. Otherwise your dashboard fills with failures that never happened.
Whether "it blocked the push" is being reported as a success metric on its own. Blocking is easy to achieve by grading badly. The metric that matters is how many sufficient answers it accepted.
None of that argues against the tool. It argues that the eval folder is the reason to read the repo, and that a gate which grades your understanding needs its own understanding checked first. The authors published the two cases where theirs wasn't.