Free

Poprosiłem Claude'a, żeby naprawił lint — w gratisie dostałem półżywe CI

Poprosiłem Claude'a o jedną poprawkę lint. Wrócił z dawno martwym CI i CVE załatanym przy okazji — skutki uboczne przyćmiły samo zadanie.


W repo how2claude zabierałem się za małą porcję edycji, a krokiem wstępnym było przejście rubocop lint. Zrobiłem screen czerwonego CI w GitHub Actions i wysłałem Claude'owi.

Jego pierwsze zdanie:

CI leży. Idę zobaczyć dlaczego.

Spodziewałem się, że odpali rubocop, popatrzy na błędy i naprawi. Nie.

Kilka minut później wrócił z czymś takim:

CI leży już od jakiegoś czasu (poprzedni run z 12 kwietnia też zafailował). Dwie klasy problemów:

Co właśnie wprowadziłem: 2 naruszenia RuboCop w plikach migracji

Co było już zepsute wcześniej:

  • CI nie ma skonfigurowanego postgres service, więc test/system-test nawet nie startuje (problem infry, jest tak od 2 tygodni)
  • 61 wcześniej istniejących naruszeń RuboCop (w plikach, których nie tknąłem)

Najpierw naprawiam 2, które wprowadziłem ja.

Na tym akapicie warto się zatrzymać na sekundę.

To, o co go prosiłem, to „napraw rubocop." Idąc obejrzeć historię CI zrobił coś, o co nie prosiłem — posegregował „dlaczego to leży" do dwóch wiader: co wprowadziłem tym razem ja vs co repo ciągnęło ze sobą od dawna.

Tej klasyfikacji nie zamawiałem. I tak ją zrobił.

Trzy tygodnie w czerwieni

Z dwóch wiader sednem było drugie:

  • W workflow CI nie było skonfigurowanego postgres service, więc db:test:prepare co run wpadał w connection refused
  • Job system-test dziedziczył ten sam crash
  • Plus 61 naruszeń RuboCop rozsianych po plikach, których nigdy nie tknąłem

Jego słowami:

Poprzedni run z 12 kwietnia też zafailował.

CI był na czerwono trzy tygodnie. Pushowałem codziennie. Każdy push pokazywał czerwone CI, ale ponieważ na ogół commituję prosto na master i specy odpalam lokalnie, wytrenowałem oko, żeby traktować to czerwone jako szum tła.

W tej samej sekundzie, w której proszę Claude'a o naprawę jednego lint, on wyciąga historię i wpycha mi pod nos „to nie zaczęło się od twojej zmiany."

Przy okazji zlikwidował CVE

Wszystko zrobił w jednym przejściu:

✓ 63 naruszenia RuboCop → 0 (autocorrect whitespace na 17 plikach)
✓ MCP gem CVE-2026-33946 → bundle update mcp, 0.9.0 → 0.14.0
✓ test/system-test bez postgres service → dodano postgres:17 service container
✓ Wszystkie 241 testy nadal przechodzą lokalnie
✓ bundler-audit: No vulnerabilities found

Spójrz na drugą linijkę. Bumpnął mcp gem z własnej inicjatywy, bo zobaczył, że nowa wersja zamyka CVE. Nie prosiłem. Po prostu zobaczył ostrzeżenie odpalając bundler-audit i zabrał je ze sobą.

Trzecia linijka jest tym, co naprawdę naprawiło CI: postgres:17 jako service container w workflow, z health check i DATABASE_URL.

Potem zapytał o granulację commitów:

Dwa commity czy jeden?

  • chore(lint): rubocop autocorrect (whitespace only)
  • fix(ci): add postgres service + bump mcp gem for CVE-2026-33946

Albo jeden: fix(ci): make CI green again?

Odpowiedziałem jednym słowem — commit — w sensie: idź dwoma osobnymi. Wypushował.

Cztery zielone, jeden czerwony

CI ruszył ponownie. Powiedziałem mu, żeby pilnował.

4 zielone, system-test wciąż czerwony. Patrzę.

Po paru sondażach wrócił z tym:

Katalog test/system/ w ogóle nie istnieje — ten job odpala test:system bez ani jednego system testu. Po prostu wywalmy job.

Kilka sekund patrzyłem na tę linijkę.

test/system/ to domyślny katalog Rails na system testy. Nigdy nie napisałem ani jednego system testu w tym projekcie, od dnia, w którym go init-owałem. A w ci.yml siedział system-test job, który odpalał się przy każdym pushu i za każdym razem zafailował, bo bundle exec rails test:system nie znajdował katalogu.

Ten job nie zepsuł się, kiedy Claude dodał postgres. Był zepsuty od dnia 1 projektu. Zakładałem, że z postgresem zazieleni się — nie, postgres nie był problemem. Problemem było to, że job trzeba usunąć.

Jego trzeci commit:

e1d91d1 chore(ci): drop system-test job

CI zielony.

Skutki uboczne są warte więcej niż samo zadanie

Jeśli spojrzeć na ten przebieg:

  • Poprosiłem o 1 rzecz: naprawić rubocop
  • Zrobił po fakcie 4: naprawił lint / załatał CVE mcp / dodał postgres service / usunął job system-test, który kręcił się na pusto

Gdybym robił to sam, zrobiłbym tylko 1 — bo czerwone CI widziałem od trzech tygodni, oko mi się przyzwyczaiło, i powiedziałbym „naprawiam lint i lecimy dalej." Wytrenowałem mózg, żeby traktować ten czerwony jak dekorację.

Claude tego nawyku ze sobą nie ciągnie. Nie ma pamięci „zawsze tak było" o tym repo. Za każdym razem, kiedy patrzy na CI, patrzy świeżym okiem. Właśnie to jest przewagą — to, co u mnie stało się „dawno zepsute domyślnie," u niego widać.

Kiedy podajesz Claude'owi małe zadanie, najcenniejszym skutkiem ubocznym często nie jest samo zadanie — tylko przy okazji zrobiony przegląd zdrowia. Jeśli przy okazji X mówi „przy okazji zauważyłem, że Y i Z też leżą od dłuższego czasu," nie spisuj tego na szum. To diagnoza.

Co się z tego stało nawykiem

Parę drobnych regulacji, które wziąłem do siebie:

Przed małym zadaniem każ mu raz przeskanować otoczenie. Niech zerknie na historię CI przed odpaleniem linta. Niech zobaczy, kiedy ostatnio dotykał schema, zanim napisze migrację. Niech zgrepnie test coverage kontrolera, zanim go zacznie edytować. „Rzut oka przed startem" kosztuje prawie nic, a dość często wyrzuca „chwila, tu coś jest."

Kiedy mówi „dwie klasy problemów" albo „dwa typy przyczyn," właśnie organizuje coś, czego nie zamawiałeś. Zatrzymaj się i przeczytaj ten akapit do końca. Nie przeskakuj.

Nie omijaj długoletniego czerwonego. Mówiłem sobie „to known issue, popatrzę później" przy każdym pushu. „Później" może oznaczać za trzy lata. Pozwolić Claude'owi posprzątać przy okazji, skoro już jest w pobliżu, jest dużo bardziej realistyczne niż wykrojenie slotu na „idę naprawiać CI."

Te trzy commity, jeszcze raz:

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

Pierwszy to to, o co prosiłem. Drugi i trzeci to to, co przyniósł sam. Te dwa są warte dużo więcej niż pierwszy.