Bugs diagnostizieren
Eine Disziplin für schwierige Bugs. Überspringe Phasen nur, wenn es explizit gerechtfertigt ist.
Beim Erkunden der Codebasis lies CONTEXT.md (falls vorhanden), um ein klares mentales Modell der relevanten Module zu erhalten, und überprüfe ADRs in dem Bereich, den du berührst.
Redigieren
Dieses Skill zeigt dir Befehle, Ausgaben und erfasste Artefakte an. Redigiere zuerst jedes Geheimnis: schreibe <REDACTED> an dessen Stelle. Baue Schleifen gegen Umgebungsvariablen, damit die Anmeldedaten in der Umgebung bleiben und nicht in dem, was du zeigst. Erfasste Artefakte enthalten Auth-Header: zitiere nur die Zeilen, die das Signal tragen.
Wenn die redigierte Ausgabe nicht ausreicht, um den Bug zu diagnostizieren, sage dies und frage den Benutzer.
Phase 1: Baue eine Rückkopplungsschleife
Dies ist das Skill. Alles andere ist mechanisch. Wenn du ein enges Bestanden-/Fehlgeschlagen-Signal für den Bug hast (eines, das bei diesem Bug rot wird), wirst du die Ursache finden; Bisektion, Hypothesentest und Instrumentierung verbrauchen es alle nur. Wenn du keins hast, wird dir kein noch so langes Starren auf Code helfen.
Investiere hier überproportional viel Mühe. Sei aggressiv. Sei kreativ. Weigere dich aufzugeben.
Möglichkeiten, eine zu konstruieren, ungefähr in dieser Reihenfolge
- Fehlschlagender Test an der Nahtstelle, die den Bug erreicht: Unit, Integration, E2E.
- Curl / HTTP-Skript gegen einen laufenden Dev-Server.
- CLI-Aufruf mit einer Fixture-Eingabe, der stdout gegen einen bekannten Snapshot diffed.
- Headless-Browser-Skript (Playwright / Puppeteer), das die UI steuert und auf DOM/Konsole/Netzwerk assertet.
- Wiedergabe einer aufgezeichneten Spur. Speichere eine echte Netzwerkanfrage / Nutzlast / Ereignisprotokoll auf die Festplatte; spiele sie isoliert durch den Codepfad ab.
- Wegwerf-Harness. Starte eine minimale Teilmenge des Systems (ein Dienst, gemockte Abhängigkeiten), die den Bug-Codepfad mit einem einzigen Funktionsaufruf ausführt.
- Eigenschafts-/Fuzz-Schleife. Wenn der Bug „manchmal falsche Ausgabe“ ist, führe 1000 zufällige Eingaben aus und suche nach dem Fehlermodus.
- Bisektions-Harness. Wenn der Bug zwischen zwei bekannten Zuständen (Commit, Datensatz, Version) aufgetreten ist, automatisiere „Boote bei Zustand X, überprüfe, wiederhole“, damit du es mit
git bisect runausführen kannst. - Differentialschleife. Führe dieselbe Eingabe durch alte Version vs. neue Version (oder zwei Konfigurationen) aus und diffe die Ausgaben.
- HITL-Bash-Skript. Letzter Ausweg. Wenn ein Mensch klicken muss, steuere sie mit
scripts/hitl-loop.template.sh, damit die Schleife trotzdem strukturiert bleibt. Erfasste Ausgaben fließen zurück zu dir.
Baue die richtige Rückkopplungsschleife, und der Bug ist zu 90% behoben.
Die Schleife enger machen
Behandle die Schleife wie ein Produkt. Sobald du eine Schleife hast, mache sie enger:
- Kann ich sie schneller machen? (Setup cachen, irrelevante Initialisierung überspringen, den Testumfang eingrenzen.)
- Kann ich das Signal schärfer machen? (Auf das spezifische Symptom asserten, nicht auf „ist nicht abgestürzt“.)
- Kann ich sie deterministischer machen? (Zeit fixieren, RNG seeden, Dateisystem isolieren, Netzwerk einfrieren.)
Eine 30-sekündige, flaky-Schleife ist kaum besser als keine Schleife; eine 2-sekündige deterministische ist eng, eine Debugging-Superkraft.
Nichtdeterministische Bugs
Das Ziel ist keine saubere Reproduktion, sondern eine höhere Reproduktionsrate. Wiederhole den Auslöser 100×, parallelisiere, füge Last hinzu, verkleinere Zeitfenster, injiziere Schlafpausen. Ein Bug mit 50% Flaky-Rate ist debuggbar; 1% ist es nicht, also erhöhe die Rate weiter, bis er debuggbar ist.
Wenn du wirklich keine Schleife bauen kannst
Stoppe und sage dies explizit. Liste auf, was du versucht hast. Frage den Benutzer nach: (a) Zugriff auf die Umgebung, die ihn reproduziert, (b) einem redigierten erfassten Artefakt (HAR-Datei, Protokolldump, Core-Dump, Bildschirmaufzeichnung mit Zeitstempeln) oder (c) Erlaubnis, temporäre Produktionsinstrumentierung hinzuzufügen. Fahre nicht mit Hypothesen fort, ohne eine Schleife.
Fertigstellungskriterium: eine enge Schleife, die rot wird
Phase 1 ist abgeschlossen, wenn die Schleife eng und rotfähig ist: du kannst einen Befehl nennen (einen Skriptpfad, einen Testaufruf, einen curl), den du bereits mindestens einmal ausgeführt hast (zeige den Aufruf und seine Ausgabe, redigiert), und der:
- Rotfähig ist: er treibt den tatsächlichen Bug-Codepfad an und assertet das vom Benutzer genannte genaue Symptom, sodass er bei diesem Bug rot werden und nach der Behebung grün werden kann. Nicht „läuft ohne Fehler“; er muss diesen spezifischen Bug abfangen können.
- Deterministisch ist: jedes Mal das gleiche Urteil (flaky Bugs: eine festgelegte, hohe Reproduktionsrate, siehe oben).
- Schnell ist: Sekunden, nicht Minuten.
- Agent-ausführbar ist: du kannst ihn unbeaufsichtigt ausführen; ein Mensch ist nur über
scripts/hitl-loop.template.shin der Schleife.
Wenn du dich dabei ertappst, Code zu lesen, um eine Theorie zu entwickeln, bevor dieser Befehl existiert, stoppe: direkt zu einer Hypothese zu springen, ist genau das Versagen, das dieses Skill verhindert. Kein rotsignal-fähiger Befehl, keine Phase 2.
Phase 2: Reproduzieren + Minimieren
Führe die Schleife aus. Beobachte, wie sie rot wird, während der Bug auftritt.
Bestätige:
- Die Schleife produziert den vom Benutzer beschriebenen Fehlermodus, nicht einen anderen Fehler, der zufällig in der Nähe liegt. Falscher Bug = falsche Behebung.
- Der Fehler ist über mehrere Läufe hinweg reproduzierbar (oder bei nichtdeterministischen Bugs mit einer ausreichend hohen Rate, um dagegen zu debuggen).
- Du hast das genaue Symptom erfasst (Fehlermeldung, falsche Ausgabe, langsames Timing), damit spätere Phasen überprüfen können, ob der Fix tatsächlich darauf eingeht.
Minimieren
Sobald er rot ist, schrumpfe die Reproduktion auf das kleinste Szenario, das immer noch rot wird. Schneide Eingaben, Aufrufer, Konfiguration, Daten und Schritte einzeln nacheinander weg, führe die Schleife nach jedem Schnitt erneut aus und behalte nur das, was für den Fehler tragend ist.
Warum der Aufwand: Eine minimale Reproduktion verkleinert den Hypothesenraum in Phase 3 (weniger bewegliche Teile, die verdächtigt werden müssen) und wird zum sauberen Regressionstest in Phase 5.
Fertig, wenn jedes verbleibende Element tragend ist: das Entfernen eines einzelnen führt dazu, dass die Schleife grün wird.
Fahre nicht fort, bis du reproduziert und minimiert hast.
Phase 3: Hypothesen aufstellen
Erzeuge 3–5 geordnete Hypothesen, bevor du eine von ihnen testest. Die Erzeugung einer einzelnen Hypothese verankert sich an der ersten plausiblen Idee.
Jede Hypothese muss falsifizierbar sein: gib die Vorhersage an, die sie macht.
Format: „Wenn die Ursache ist, dann wird <Ändern von Y> den Bug verschwinden lassen / <Ändern von Z> wird ihn verschlimmern.“
Wenn du die Vorhersage nicht formulieren kannst, ist die Hypothese ein Gefühl: verwerfe sie oder schärfe sie.
Zeige dem Benutzer die geordnete Liste vor dem Testen. Sie haben oft Domänenwissen, das die Reihenfolge sofort ändert („wir haben gerade eine Änderung an #3 ausgerollt“) oder kennen Hypothesen, die sie bereits ausgeschlossen haben. Günstiger Checkpoint, große Zeitersparnis. Blockiere nicht darauf; fahre mit deiner Reihenfolge fort, wenn der Benutzer nicht da ist.
Phase 4: Instrumentieren
Jede Sonde muss einer bestimmten Vorhersage aus Phase 3 zugeordnet sein. Ändere jeweils eine Variable.
Werkzeugpräferenz:
- Debugger / REPL-Inspektion, wenn die Umgebung dies unterstützt. Ein Haltepunkt schlägt zehn Logs.
- Gezielte Logs an den Grenzen, die Hypothesen unterscheiden.
- Niemals „alles loggen und grep“.
Markiere jedes Debug-Log mit einem eindeutigen Präfix, z. B. [DEBUG-a4f2]. Die Bereinigung am Ende wird zu einem einzigen grep. Nicht markierte Logs überleben; markierte Logs sterben.
Perf-Branch. Bei Performance-Regressionen sind Logs meist falsch. Stattdessen: Etabliere eine Basismessung (Timing-Harness, performance.now(), Profiler, Abfrageplan), dann bisektiere. Messe zuerst, repariere später.
Phase 5: Fix + Regressionstest
Schreibe den Regressionstest vor dem Fix, aber nur, wenn es eine korrekte Nahtstelle dafür gibt.
Eine korrekte Nahtstelle ist eine, bei der der Test das echte Bug-Muster so übt, wie es am Aufrufort auftritt. Wenn die einzige verfügbare Nahtstelle zu flach ist (Test mit einem einzigen Aufrufer, wenn der Bug mehrere Aufrufer benötigt, Unit-Test, der die Kette, die den Bug ausgelöst hat, nicht replizieren kann), gibt ein Regressionstest dort falsche Sicherheit.
Wenn keine korrekte Nahtstelle existiert, ist das selbst der Befund. Notiere es. Die Codebasis-Architektur verhindert, dass der Bug festgenagelt wird. Melde dies für die nächste Phase.
Wenn eine korrekte Nahtstelle existiert:
- Verwandle die minimierte Reproduktion in einen fehlschlagenden Test an dieser Nahtstelle.
- Beobachte, wie er fehlschlägt.
- Wende den Fix an.
- Beobachte, wie er besteht.
- Führe die Rückkopplungsschleife aus Phase 1 erneut gegen das ursprüngliche (nicht minimierte) Szenario aus.
Phase 6: Aufräumen
Erforderlich, bevor als abgeschlossen erklärt wird:
- Ursprüngliche Reproduktion reproduziert nicht mehr (führe die Phase-1-Schleife erneut aus)
- Regressionstest besteht (oder Fehlen einer Nahtstelle ist dokumentiert)
- Alle
[DEBUG-...]-Instrumentierung entfernt (grepdas Präfix) - Wegwerf-Prototypen gelöscht (oder in ein klar markiertes Debug-Verzeichnis verschoben)
- Die Hypothese, die sich als richtig herausgestellt hat, wird in der Commit- / PR-Nachricht angegeben, damit der nächste Debugger lernt