Free

Попросил Claude починить лит, а он вытащил наружу полудохлый CI

Попросил у Claude правку одной строки линта. Он вернулся с давно мёртвой CI и попутно прикрытым CVE — побочные эффекты затмили задачу.


В репо how2claude я собирался сделать небольшую серию правок, и предварительный шаг — пройти rubocop lint. Я скриншотнул красный GitHub Actions CI и кинул Claude.

Его первая фраза:

CI упал. Сейчас посмотрю почему.

Я думал, он запустит rubocop, посмотрит ошибки и починит. Не так.

Через несколько минут он вернулся с этим:

CI давно лежит (предыдущий run от 12 апреля тоже зафейлен). Два класса проблем:

То, что я только что притащил: 2 нарушения RuboCop в файлах миграций

То, что было сломано раньше:

  • В CI не настроен postgres service, поэтому test/system-test даже не стартуют (проблема инфры, в таком виде уже 2 недели)
  • 61 преже существовавшее нарушение RuboCop (в файлах, которые я не трогал)

Сначала чиню 2, которые притащил я.

На этом абзаце стоит остановиться на секунду.

Я попросил его «почини rubocop». Заглянув в историю CI, он сделал то, о чём я не просил, — рассортировал «почему оно сломано» по двум корзинам: что я притащил в этот раз vs что репо уже давно тащит за собой.

Эту классификацию я не запрашивал. Он её всё равно сделал.

Три недели в красном

Из двух корзин по сути важна вторая:

  • В CI-workflow не было postgres service, поэтому db:test:prepare каждый раз падал в connection refused
  • system-test job наследовал тот же крах
  • Плюс 61 нарушение RuboCop, разбросанное по файлам, которые я никогда не трогал

Его словами:

Предыдущий run от 12 апреля тоже зафейлен.

CI был в красном три недели. Я каждый день пушил. Каждый push показывал красный CI, но поскольку я обычно коммичу прямо в master и гоняю спеки локально, я натренировал глаз воспринимать этот красный как фоновый шум.

И в ту самую секунду, когда я прошу Claude починить один линт, он подтягивает историю и тычет мне в лицо: «это не началось с твоего изменения».

Заодно прибил один CVE

Он всё это провернул в один заход:

✓ 63 нарушения RuboCop → 0 (autocorrect whitespace по 17 файлам)
✓ MCP gem CVE-2026-33946 → bundle update mcp, 0.9.0 → 0.14.0
✓ test/system-test без postgres service → добавлен postgres:17 service container
✓ Все 241 теста локально по-прежнему проходят
✓ bundler-audit: No vulnerabilities found

Посмотри на вторую строчку. Он по своей инициативе бампнул mcp gem, потому что увидел, что в новой версии закрыли CVE. Я не просил. Он просто увидел warning при bundler-audit и забрал это с собой.

Третья строка — то, что реально починило CI: postgres:17 как service container в workflow, с health check и DATABASE_URL.

Дальше он спросил про гранулярность коммитов:

Два коммита или один?

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

Или один: fix(ci): make CI green again?

Я ответил одним словом — commit — то есть: иди с двумя раздельными. Он запушил.

Четыре зелёных, один красный

CI перезапустился. Я попросил его следить.

4 зелёных, system-test ещё красный. Смотрю.

После пары проб вернулся с:

Каталога test/system/ вообще нет — этот job гоняет test:system, при этом ни одного system-теста нет. Просто удаляем джоб.

Я несколько секунд смотрел на эту строчку.

test/system/ — это дефолтная директория для system-тестов в Rails. В этом проекте я ни разу не написал ни одного system-теста с того дня, как сделал init. А в ci.yml висел system-test job, который запускался на каждый push и каждый раз падал, потому что bundle exec rails test:system не находил каталог.

Этот job сломался не тогда, когда Claude добавил postgres. Он был сломан с первого дня проекта. Я предполагал, что с postgres он позеленеет — нет, postgres не был проблемой. Проблема была в том, что джоб надо удалить.

Его третий коммит:

e1d91d1 chore(ci): drop system-test job

CI зелёный.

Побочные эффекты ценнее самой задачи

Если оглянуться на этот заход:

  • Я попросил 1 вещь: починить rubocop
  • Он сделал по факту 4: починил линт / закрыл CVE mcp / добавил postgres service / удалил впустую крутившийся system-test job

Если бы я делал это сам, сделал бы только 1 — потому что красный CI я смотрел три недели, глаз затёрся, и я бы сказал «починю линт и поехали». У меня выработалась привычка воспринимать этот красный как декорации.

У Claude такой привычки нет. У него нет памяти «оно всегда было такое» про этот репо. Каждый раз, когда он смотрит CI, он смотрит свежим взглядом. Именно это и есть преимущество — то, что для меня стало «давно сломано по умолчанию», для него видно.

Когда передаёшь Claude маленькую задачу, самый ценный побочный продукт часто не сама задача — а попутный осмотр здоровья. Если он по ходу X говорит «попутно заметил, что Y и Z тоже давно лежат», не списывай это в шум. Это диагноз.

Что превратилось в привычку

Несколько мелких настроек, которые я взял в работу:

Перед маленькой задачей пусть прежде разок просканирует окрестности. Перед запуском линта — пусть посмотрит историю CI. Перед написанием миграции — когда последний раз менялась schema. Перед редактированием контроллера — пусть пройдёт grep по покрытию тестами этого файла. «Беглый взгляд перед стартом» стоит почти ничего, а довольно часто выдаёт «погоди, тут что-то есть».

Когда он говорит «два класса проблем» или «два типа причин», это он делает ту самую упаковку, которую ты не заказывал. Притормози и прочти этот абзац целиком. Не пролистывай.

Не объезжай долгоиграющий красный. Я говорил себе «это known issue, посмотрю позже» при каждом push. «Позже» может означать через три года. Дать Claude убрать это попутно, раз он рядом, гораздо реалистичнее, чем выкраивать слот «иду чинить CI».

Те три коммита, ещё раз:

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

Первый — то, о чём я просил. Второй и третий — то, что он притащил сам. Эти двое стоят гораздо больше первого.