Ich habe Claude um eine Lint-Korrektur gebeten. Er kam mit einer seit langem toten CI und einem nebenbei gepatchten CVE zurück — die Beifänge stellten die Aufgabe in den Schatten.
Im how2claude-Repo wollte ich gerade eine kleine Reihe Edits machen, und der Vorlauf war, das rubocop-Lint durchzubekommen. Ich habe einen Screenshot der roten GitHub-Actions-CI gemacht und ihn an Claude geschickt.
Sein erster Satz:
CI ist kaputt. Ich schaue mal warum.
Ich nahm an, er würde rubocop laufen lassen, sich die Fehler ansehen und sie reparieren. Tat er nicht.
Ein paar Minuten später kam das hier zurück:
CI war schon eine Weile kaputt (auch der Run vom 12. April davor ist gefailt). Zwei Klassen von Problemen:
Was ich gerade neu eingebracht habe: 2 RuboCop-Verstöße in Migration-Dateien
Was schon vorher kaputt war:
- Die CI hat keinen postgres-Service konfiguriert, also kommen test/system-test gar nicht erst hoch (Infra-Problem, ist seit 2 Wochen so)
- 61 vorbestehende RuboCop-Verstöße (in Dateien, die ich nicht angefasst habe)
Ich fange mit den 2 an, die ich eingebracht habe.
Bei diesem Absatz lohnt sich eine Sekunde Pause.
Ich hatte ihn um „repariere rubocop" gebeten. Beim Durchsehen der CI-History hat er etwas gemacht, was ich nicht verlangt hatte — er hat das „Warum ist das kaputt?" in zwei Eimer sortiert: Was ich diesmal eingeschleppt habe vs. was das Repo schon lange mitschleppt.
Diese Klassifizierung habe ich nicht angefordert. Er hat sie trotzdem gemacht.
Von beiden Eimern war der zweite das eigentliche Thema:
db:test:prepare jedes Mal in connection refusedIn seinen Worten:
Auch der Run vom 12. April davor ist gefailt.
CI war drei Wochen rot. Ich hatte jeden Tag gepusht. Jeder Push zeigte rote CI, aber weil ich meistens direkt auf master committe und Specs lokal laufen lasse, hatte ich mein Auge darauf trainiert, dieses Rot als Hintergrundrauschen abzutun.
Genau in dem Moment, als ich Claude um einen Lint bitte, zieht er die History und schiebt mir „das hat nicht mit deiner Änderung angefangen" unter die Nase.
Er hat alles in einem Rutsch durchgezogen:
✓ 63 RuboCop-Verstöße → 0 (Whitespace-Autocorrect über 17 Dateien)
✓ MCP gem CVE-2026-33946 → bundle update mcp, 0.9.0 → 0.14.0
✓ test/system-test ohne postgres-Service → postgres:17-Service-Container ergänzt
✓ Alle 241 Tests laufen lokal weiterhin durch
✓ bundler-audit: No vulnerabilities found
Schau auf Zeile zwei. Er hat das mcp-gem von sich aus gebumpt, weil er gesehen hat, dass die neue Version einen CVE schließt. Das hatte ich nicht verlangt. Er hat einfach beim bundler-audit die Warnung gesehen und sie gleich mitgenommen.
Zeile drei ist das, was die CI wirklich repariert: postgres:17 als Service-Container im Workflow, mit Health Check und DATABASE_URL.
Dann hat er nach Commit-Granularität gefragt:
Zwei Commits oder einer?
chore(lint): rubocop autocorrect (whitespace only)fix(ci): add postgres service + bump mcp gem for CVE-2026-33946Oder einer:
fix(ci): make CI green again?
Ich habe mit einem Wort geantwortet — commit — also: nimm die zwei getrennten. Er hat gepusht.
CI lief erneut. Ich habe ihn beobachten lassen.
4 grün, system-test noch rot. Schaue.
Nach ein paar Sondierungen kam:
test/system/existiert nicht einmal — der Job läufttest:system, aber es gibt überhaupt keine System-Tests. Lass uns den Job einfach löschen.
Ich habe ein paar Sekunden auf diese Zeile gestarrt.
test/system/ ist das Default-Verzeichnis für Rails-System-Tests. In diesem Projekt habe ich nie einen System-Test geschrieben, kein einziges Mal seit dem Tag, an dem ich es initialisiert habe. Aber das ci.yml hatte einen system-test-Job, der bei jedem Push lief und jedes Mal scheiterte, weil bundle exec rails test:system das Verzeichnis nicht fand.
Der Job war nicht erst kaputt, als Claude postgres ergänzt hat. Er war seit Tag 1 des Projekts kaputt. Ich hatte angenommen, mit postgres würde er grün werden — nein, postgres war nicht das Problem. Das Problem war: Der Job musste weg.
Sein dritter Commit:
e1d91d1 chore(ci): drop system-test job
CI grün.
Wenn ich auf den Lauf zurückblicke:
Hätte ich es allein gemacht, hätte ich nur die 1 erledigt — weil ich seit drei Wochen rote CI sehe, mein Auge taub war, und ich „repariere den Lint und gut" gesagt hätte. Ich hatte mir antrainiert, dieses Rot als Kulisse zu sehen.
Claude trägt diese Gewohnheit nicht. Er hat keinerlei „das war schon immer so"-Erinnerung an das Repo. Jedes Mal, wenn er die CI ansieht, sieht er sie frisch. Das ist genau der Vorteil — was bei mir längst zum „dauer-defekten Default" geworden war, sieht er.
Wenn du Claude eine kleine Aufgabe gibst, ist der wertvollste Beifang oft nicht die Aufgabe selbst — sondern der nebenbei laufende Gesundheits-Check. Wenn er dir sagt „während ich X gemacht habe, ist mir aufgefallen, dass auch Y und Z schon eine Weile kaputt sind," wirf das nicht als Rauschen weg. Das ist die Diagnose.
Ein paar kleine Sachen, die ich übernommen habe:
Lass ihn vor der kleinen Aufgabe einmal die Umgebung scannen. Soll er die CI-History ansehen, bevor er Lint laufen lässt. Soll er nachsehen, wann das Schema zuletzt angefasst wurde, bevor er eine Migration schreibt. Soll er die Test-Coverage eines Controllers greppen, bevor er ihn editiert. „Ein Blick vor dem Start" kostet fast nichts und fördert ziemlich oft ein „Moment, da ist was" zutage.
Wenn er sagt „zwei Klassen von Problemen" oder „zwei Arten von Ursachen," dann sortiert er etwas, was du nicht angefordert hast. Halt an, lies den Absatz zu Ende. Nicht überfliegen.
Lange-rote Stellen nicht umgehen. Ich hatte mir bei jedem Push gesagt „das ist ein bekanntes Problem, schaue ich später an." „Später" kann in drei Jahren sein. Claude im Vorbeigehen aufräumen zu lassen ist deutlich realistischer, als sich einen Slot freizuräumen, um „die CI zu reparieren."
Die drei Commits, noch einmal:
74a3ebf chore(lint): rubocop autocorrect — Layout cops only
76264ee fix(ci): add postgres service + bump mcp gem for CVE-2026-33946
e1d91d1 chore(ci): drop system-test job
Der erste ist das, worum ich gebeten habe. Der zweite und dritte sind das, was er von sich aus mitgebracht hat. Diese beiden sind weit mehr wert als der erste.