AI-ревьюер для GitLab, который проверяет не отдельный merge request, а всю задачу сразу, во всех сервисах, которые она задела. И почему для этого понадобился агент, а не анализ diff через API.
Из своих двадцати лет в ИТ лет пятнадцать я работаю удалённо. Последний раз из офиса ушёл десять лет назад. Тогда я ездил из Москвы на дачу в понедельник утром, как раз когда пробка стоит в сторону Москвы. Сочувственно глядел на встречку и с удовольствием жал на газ. А чтобы в выходные не слушать газонокосилки соседей, возвращался в опустевший город в субботу утром. И снова сочувственно смотрел на встречку: там стояли те же самые люди, только теперь они ехали на дачу.
Что-то есть в том, чтобы идти против течения.
Сейчас многие переживают, что их заменит нейросеть. Сочувственно смотрю, потому что понимаю, что при таком отношении точно заменят. И при этом сам стараюсь заменить себя нейросетью везде, где только получается. Это высвобождает время на новые задачи, я успеваю принести больше пользы. Да и, по большому счёту, в этом теперь и есть суть работы: автоматизировать всю интеллектуальную рутину. Ревью кода у лида в этой рутине занимает весьма жирный кусок. С него и начал.
Расскажу все подробно и в конце дам ссылку на готовое настроенное решение, которое подключается к проектам еще элегантнее, чем брюки превращаются…
Оговорка сразу
Это решение заточено под GitLab и ни под что другое: оно опирается на trigger API, pipeline inputs, тексты и комменты к issue, award emoji и модель переменных GitLab CI. С GitHub и Bitbucket не заработает.
Зато внутри GitLab оно универсальное:
работает на gitlab.com и на self-managed, включая Free/CE (нужен GitLab 17.11+, для gitlab.com это всегда так);
рассчитано на группу проектов: типичная картина, когда у команды есть десяток-другой сервисов, фронт, библиотеки контрактов, и одна задача регулярно задевает три-четыре из них;
подходит под любые языки: ревьюер ничего не знает о вашем стеке, он читает код так же, как читал бы человек.
Проблема: ревьюер проверяет код проекта, а ломается стык
Возьмём обычную задачу: «добавить в биллинг новый статус платежа». Меняется сервис биллинга, меняется схема события, меняется потребитель этого события, меняется фронт. Четыре репозитория, четыре merge request.
Что происходит дальше в большинстве команд:
каждый MR ревьюит свой человек (или один и тот же, по очереди, в разные дни);
каждый MR в отдельности выглядит корректно;
ломается то, чего нет ни в одном diff: продюсер уже шлёт новое поле, а консьюмер ещё парсит старую структуру; enum расширили в одном месте и забыли в другом; миграцию положили не в тот репозиторий.
Классические AI-ревьюеры устроены так же: берут diff одного MR, отправляют в модель, получают комментарий. Стык между сервисами им не виден в принципе.
Что сделали
Ревью запускается на MR, но проверяет задачу:
По MR находим задачу (issue).
По истории issue находим все репозитории группы, где под эту задачу есть ветки.
Клонируем их все в одно рабочее пространство, рядом кладём описание задачи с обсуждением и готовые diff’ы.
Запускаем агента: полноценный CLI (Codex, Claude Code, Gemini CLI, …), который сам ходит по файлам, грепает вызовы, открывает соседние модули.
Агент пишет отчёт в MR и голосует 👍 или 👎.
Merge разрешается по кворуму «2 из 3»: AI + один человек или два человека, если ИИ забраковал MR.
Для разработчика это выглядит так: создал ветку 1085-new-payment-status из issue, запушил, открыл MR, и через несколько минут в MR комментарий вида:
## Summary ... ## Blocking issues 1. `billing-consumer/app/events/payment.py:88` — новое значение `PARTIALLY_REFUNDED` из billing-api не обрабатывается, событие уйдёт в dead letter — ... ## Cross-repo compatibility Проверено: схема PaymentEvent в billing-api и billing-consumer ... VERDICT: REJECTED
Обратите внимание на путь: замечание про другой репозиторий, найденное при ревью MR в биллинге. Ради этого всё и затевалось.
Почему это экономит силы ревьюера
Человеку, ревьюящему кросс-сервисную задачу, приходится делать то же самое, что делает наш пайплайн, только руками: открыть четыре MR во вкладках, найти, где продюсер, где консьюмер, держать в голове контракт. Именно эта часть самая утомительная, и именно из-за неё чаще всего пропускают баги.
Я сам так ревьюил годами: четыре вкладки, в голове схема события, через полчаса уже не помнишь, в какой вкладке консьюмер. Работа нужная, но творческого в ней ноль. Ровно такую я и хотел отдать машине.
Идея такая:
одна точка входа: ревьюится один MR, отчёт один, про все репозитории;
человек читает не голый diff, а отчёт с
файл:строка, списком блокирующих проблем и выводом о совместимости сервисов;правила проекта учитываются: AI ревьюер читает
CLAUDE.md,AGENTS.md,CONTRIBUTING.md,ARCHITECTURE.md,docs/каждого репозитория и цитирует правило, которое нарушено;если AI одобрил, нужен всего один человек вместо двух.
При этом AI не имеет права вето. Об этом ниже.
Почему агент, а не «diff в API»
Первую версию я сделал именно так: git diff → модель → комментарий. Она проверяла только один проект и за пределы diff не смотрела. Ложных замечаний было около 30%. Но остальные 70% были полезными, и в сумме это экономило мне примерно 20% времени на ревью.
Это время я потратил на то, чтобы сделать ИИ-ревью в агентском режиме (то самое, про которое эта статья). С ним на ревью я трачу на 80% времени меньше. Нейросеть высвободила время, на которое я сделал нейросеть получше. Против течения, так против течения.
Причина простая: вопрос «сломал ли продюсер консьюмера» не решается чтением diff’а. Нужно открыть консьюмера. Нужно найти все места, где вызывается функция, у которой поменялась сигнатура. Нужно посмотреть, как в соседнем модуле принято обрабатывать ошибки. Никакой объём вставленного в промпт diff’а это не заменит. А агент делает ровно то, что сделал бы опытный ревьюер.
Рабочее пространство, которое получает агент:
/tmp/workspace_1085/ TASK_CONTEXT.md # описание issue + всё обсуждение DIFFS/ billing-api.diff billing-consumer.diff web.diff billing-api/ # полный клон, ветка задачи billing-consumer/ web/
И инструкция: сначала прочитай требования, потом правила каждого репозитория, потом diff, потом открой окружающий код и найди связи. Проверяем пять вещей:
соответствие задаче,
границы репозиториев и их правила,
совместимость контрактов между сервисами,
побочные эффекты,
обычные баги и безопасность.
Отдельно порадовал пункт про границы репозиториев. Код, попавший не в тот репозиторий, в изоляции выглядит безупречно, и найти такое можно, только прочитав правила. Поэтому нарушение правила из ARCHITECTURE.md у нас блокирующее, даже если сам код идеален.
Как находятся все репозитории задачи
Это, пожалуй, самая «грязная» и самая интересная часть.
Номер задачи берётся из ветки. Соглашение такое: <номер issue>-описание, ровно то, что генерирует кнопка GitLab «Create branch» в issue. Номер именно в начале: если в названии есть цифра (oauth-2-login-1085), то ведущий номер не перепутаешь ни с чем. Поддерживаются и feature/1085-fix, TASK-123-….
Соседние репозитории берутся из системных заметок issue. Когда в другом проекте делают коммит с group/billing#1085, GitLab пишет в issue «mentioned in commit group/billing-consumer@abc1234». Эти заметки парсятся, короткие формы (billing-consumer@abc1234: GitLab сокращает ссылки внутри одного namespace) достраиваются до полного пути.
Ветка определяется по коммиту: GET /repository/commits/:sha/refs отвечает, какие ветки содержат этот коммит. Никакой договорённости об именах в соседних репозиториях не требуется.
Follower merge requests
Допустим, задача трогает четыре репозитория, а ревью было одно, в том проекте, где создано issue. Как ветки соседей попадают в свой main? В GitLab код в защищённую ветку попадает только через MR.
Ревьюер открывает их сам, с меткой merge::follower, и ссылки на них держит в отдельном комментарии, который удаляется и публикуется заново после каждого ревью, чтобы он всегда был последним в треде, там, куда разработчик реально смотрит. Список оформлен чек-листом, смёрженные можно отмечать.
На follower-MR не запускается ни ревью, ни кворум: их код уже прочитан в составе ведущего. Повторное ревью не добавило бы ценности, а повторный кворум означал бы просьбу проголосовать дважды за одно решение. Там работает фиктивный job follower-review, который ничего не проверяет: он нужен, чтобы пайплайн существовал, иначе с «Pipelines must succeed» MR может оказаться несмёрживаемым.
В описании follower всегда Part of group/project!7, никогда не Closes: иначе первый смёрженный закрыл бы задачу, которая ещё не доделана.
Кворум «2 из 3»: AI голосует, но не решает
Голос AI | Нужно 👍 людей |
|---|---|
👍 | 1 |
👎 | 2 |
нет голоса (упал, не запускался) | 2 |
У AI один голос из трёх. Если не согласны с ним, позовите второго человека и мержите. Это принципиально: ложное срабатывание без пути обхода заставит команду отключить проверку целиком, а это хуже, чем разовый сбой AI.
Нюансы по оценкам:
Голос AI перезаписывается на каждом прогоне. Прошлое одобрение AI снимается перед следующим прогоном.
👍 автора и 👍 любых ботов не считается.
Кто такой бот, выясняется, а не конфигурируется.
Так как в GitLab CE нет API одобрений MR, роль gate выполняет job verify-approvals, а блокирует merge стандартная настройка «Pipelines must succeed».
Работа с возражениями
Конечно же, когда я встроил это ИИ-ревью в проекты, коллеги на меня обрушились.
«Коля, твой ИИ находит какую-то фигню, напрасно ругается». Открываем MR, разбираемся. Оказывается, разработчик использовал не самый очевидный вариант интеграции, и ИИ это не понравилось. Прикол в том, что и мне как ревьюеру это было не очевидно: причина лежит за пределами кода. Договорились, что в местах, где решение по коду не очевидно, пишем комментарий прямо в коде. ИИ-ревью после этого проходит.
«Коля, ИИ ерунду говорит: я сделал задачу не так, как в описании, были же уточнения». Открываем задачу в GitLab, и правда, описание и реализация разошлись. Ну ок. А как тестировщику понять, что проверять? Как потомкам найти концы, если они сюда вернутся? Договорились дописывать в задачу, что и почему поменялось. ИИ-ревью этому был рад, и на тестировании вопросов не возникло.
«Коля, ИИ опять ругается, а в коде всё хорошо!» Смотрим код вместе и видим, что ИИ ругается по делу. Код уходит на доработку.
Заметьте: в первых двух случаях ИИ подсветил не баг, а дыру в контексте. Ту же самую, на которую потом наступил бы человек, открывший этот код или эту задачу.
Через ревью прошло больше сотни задач, и ни одной ошибки от ИИ-ревью не было. Мне стало гораздо спокойнее деплоить: после этого ревью я сам по коду багов уже не нахожу. А раньше находил регулярно.
Самое интересное технически
1. Два пайплайна, потому что переменные CI не секретны
Самое неочевидное архитектурное решение. Переменную CI/CD может прочитать любой, кто может запушить ветку и запустить job: echo "$SECRET" | curl …, и маскировка не поможет, она лишь скрывает значение в логе. Protected-переменные от этого защищают, но они невидимы для MR-пайплайнов, которые идут на незащищённых ветках.
Отдавать ключ модели и write-токен бота каждому разработчику группы не хотелось. Поэтому ревью идёт во втором пайплайне, в проекте ревьюера:
проверяемый проект, MR проект-ревьюер ────────────────────── ─────────────────────────────── ai-review ── trigger API ─────────▶ run-review (только project id + MR iid) клоны, агент, комментарий, голос [токен бота и ключи моделей — Protected-переменные] verify-approvals ◀────────────────────────┘ считает голоса, read-only токен
В проверяемых проектах остаются два дешёвых для утечки токена: один умеет только запустить ревью, другой только посчитать реакции.
2. Inputs вместо variables
Тонкое место: trigger-токен лежит в групповой переменной, его может прочитать любой разработчик. А pipeline variable, переданная через trigger, перекрывает всё, включая предопределённые переменные. С разрешёнными переменными можно было бы передать CI_REGISTRY_IMAGE=мой/образ, и собственный образ выполнился бы с токеном бота и ключами моделей.
Поэтому проект-ревьюер переменные от trigger не принимает вообще («Minimum role to use pipeline variables → No one allowed»), а MR приходит двумя типизированными inputs (GitLab 17.11+), которые ещё до старта пайплайна валидируются как числа. Эта настройка на inputs не распространяется.
3. Агент без секретов
Агент работает с автоподтверждением инструментов над кодом, который написал любой, у кого есть право пуша. Значит, prompt injection в README даёт шелл. Исходим из этого: агент наследует allowlist окружения (PATH, HOME, локаль, прокси, сертификаты) и свой собственный ключ провайдера. Ни токена бота, ни ключей других провайдеров, ни CI_JOB_TOKEN, ни того, что группа нагенерила в переменных. Клоны сделаны до запуска агента, поэтому GitLab-креды ему просто не нужны.
4. Гонки
Ветка уехала, пока шло ревью. Ревью длится минуты, за это время могли запушить ещё. Перед публикацией проверяется, что head MR тот же, что был в начале. Если нет, ревью молча отменяется (
Standing down: reviewed … but the branch head is now …), ведь уже идёт более новое.Два ревью одного коммита. Перезапустили пайплайн, чтобы сменить метку модели, и два ревью одного и того же SHA идут параллельно. По SHA их не различить, по времени тоже. Решение: ревью штампует id своего пайплайна в комментарий, gate знает, какой id ждать. Плюс
resource_groupна MR, чтобы второе ревью вставало в очередь, но это уже про деньги, не про корректность.Люди уже проголосовали, но gate ждёт AI. Два 👍 людей проходят кворум при любом голосе AI, так что голос арифметически ничего не решает. Но чтобы изменение, на которое AI вот-вот написал бы возражение, нельзя было бы смёржить, так и не увидев его, ждём результат AI ревью.
5. Один лог вместо двух
Ревью идёт в одном проекте, gate в другом, и следить за MR означало бы открывать оба. Gate всё равно ждёт окончания ревью, так что он просто печатает лог run-review перед своим вердиктом. Id downstream-пайплайна знает только ai-review, он передаёт его через dotenv-артефакт. А ссылка на пайплайн печатается до ожидания: она нужна, пока gate сидит свои 15 минут, а не в разборе полётов.
И наоборот, отчёт ревью не публикуется как CI-артефакт: он цитирует код, а артефакты проекта-ревьюера видны всем, кому группа дала туда доступ, включая тех, кто сам проверяемый проект открыть не может. Отчёт публикуется только комментарием в MR, где действуют права самого GitLab.
6. Маршрутизация моделей и «не той же линейки»
По умолчанию ревьюит не Claude, и это сознательно. Если код пишут с одним ассистентом и ревьюят руками с ним же, автоматический ревьюер той же линейки добавляет меньше всего пользы: он склонен пропускать то, что уже пропустили при написании кода.
Мы же не спрашиваем, например, у продавца - правильно ли нам дали сдачу, а пересчитываем сами.
Модель переключается меткой на MR:
Метка | Модель | CLI |
|---|---|---|
по умолчанию / |
|
|
|
|
|
|
|
|
|
|
|
| DeepSeek / Qwen |
|
| дешёвая модель | быстрый проход |
Первичный CLI у каждого провайдера свой, где он есть: каждый заточен под tool-calling своих моделей.
Есть и второе мнение: REVIEWER_SECOND_OPINION=gemini запускает дополнительное ревью по тому же workspace, которое пишет свой комментарий, но не голосует. Смысл в том, чтобы сравнить на реальных MR, насколько суждения двух моделей расходятся, прежде чем думать о смене основной. В лог в конце job’а выводится сводка: кто что сказал и за сколько секунд, и отдельной строкой, если модели не согласны.
7. Отказоустойчивость в мелочах
Нет строки
VERDICT:в ответе модели, значит,REJECTED, а неAPPROVED.Опечатка в
REVIEWER_DEFAULT_MODELне ломает ревью всей группы: работает маршрут по умолчанию, но в шапке комментария прямо написано, что значение не распознано, чтобы откат никогда не выдавал себя за решение.Все переменные ревьюера идут с префиксом
REVIEWER_, включая ключи провайдеров. ИмяOPENAI_API_KEYзахочет задать кто-то ещё в группе, а групповые переменные наследуются вниз.Codex на непривилегированном контейнере не может поднять свою песочницу. Песочница по умолчанию выключена, изоляцию даёт контейнер.
Shell-executor игнорирует
image:. Каждый job это замечает (находит docker CLI, которого нет ни в одном из образов) и сам делаетdocker runтого же образа.
8. Токены на проект, а не на группу
Gate’у нужен read_api. Один групповой токен в переменной означал бы, что любой разработчик, умеющий запустить job, читает все репозитории группы. Поэтому для каждого проекта выпускается свой токен, чтобы утёкший токен не давал новых привелегий.
Выпускает их локальная утилита (make tokens-grant), идемпотентно, с ротацией в безопасном порядке: создать новый → перенаправить переменную → только потом отозвать старые. Прерванный запуск оставляет рабочий gate.
На облачном gitlab.com Free (в отличие от селф-хостед Free) project access tokens недоступны, там один групповой read-токен. Это осознанный компромисс, для маленькой команды, где все видят всё, это вполне оправдано.
Время и деньги
Одно агентное ревью по нескольким репозиториям занимает минуты, в тяжёлых случаях до 10-20 минут, и съедает заметные токены. На shared-раннерах gitlab.com это compute minutes; свой раннер обычно выгоднее. Поэтому второе мнение выключено по умолчанию.
Как подключить
Форкнуть https://gitlab.com/nick-public/code-review в свою группу, сделать форк приватным.
Завести бота (на Premium через group access token, на Free отдельным аккаунтом), trigger-токен и ключ модели.
Собрать образ ручным job
build-imageв форке.В каждом проекте группы:
include: - project: my-group/code-review ref: main file: /.gitlab-ci.yml
Включить «Pipelines must succeed».
Форк обновляется кнопкой Update fork и пересборкой образа.
Если решили ставить себе, ниже пошаговая инструкция со всеми галочками в настройках. Она же на английском лежит в разделе Quick start on GitLab.com в README. Большинство проблем при первой установке связаны с Protected-флагом на групповых переменных, которые должны быть видны MR-пайплайнам.
Пошаговая установка на gitlab.com (Free и Premium)
Условные обозначения:
my-group: ваша группа на gitlab.com, в которой лежат проекты;my-group/code-review: форк репозитория, проект-ревьюер;my-group/service-a,my-group/service-b: проекты, которые нужно ревьювить.
Подставьте свои пути. Путь проекта-ревьюера пишется только в нижнем регистре (он же путь docker-образа в registry).
0. Что проверить заранее
Тариф группы: Free или Premium/Ultimate (Group → Settings → General / Billing). От этого зависят шаги 1 и 10.
API-ключ хотя бы одного провайдера. По умолчанию ревью делает OpenAI (
gpt-5.3-codexчерез Codex CLI), нужен ключ OpenAI. Другие варианты: Anthropic, DeepSeek, Gemini, xAI (Grok), Qwen.Раннеры: shared-раннеры gitlab.com подходят. Одно ревью может длиться до 45 минут, и это расход compute minutes. Если минут мало, зарегистрируйте свой раннер (docker executor) на группу.
Локально:
git,python3,make,curl(для шага 10 на Premium).
1. Бот-аккаунт и его токены
Бот должен быть отдельной учёткой, не вашей: кворум отличает 👍 AI от 👍 человека по user id, и если токен выдан на вас, вы сможете одобрить MR в одиночку.
Вариант A: Free (group access tokens недоступны):
Зарегистрировать отдельный аккаунт на gitlab.com, например
my-group-reviewer-bot.Добавить его в группу
my-groupс ролью Reporter (Group → Manage → Members → Invite members).Под ботом: User Settings → Access tokens → создать PAT со scope
api(срок обязателен, запишите дату и ротируйте заранее). ЭтоGITLAB_BOT_TOKEN.Там же второй PAT со scope
read_api. ЭтоREVIEWER_READ_TOKEN.Узнать числовой id бота:
curl -s "https://gitlab.com/api/v4/users?username=my-group-reviewer-bot"→ полеid. ЭтоREVIEWER_BOT_USER_ID.
Вариант B: Premium/Ultimate:
Group → Settings → Access tokens: токен
ai-code-reviewer, роль Reporter, scopeapi. ЭтоGITLAB_BOT_TOKEN.REVIEWER_READ_TOKENна Premium создаётся на каждый проект отдельно, см. шаг 10.
2. Форк
Открыть https://gitlab.com/nick-public/code-review → Fork.
Namespace:
my-group, project slug:code-review(в нижнем регистре).Visibility: Private. Обязательно: лог
run-reviewсодержит вывод агента, где может цитироваться код приватных проектов.Settings → General → Visibility, project features, permissions: включены CI/CD и Container registry.
Ветка
mainзащищена (Settings → Repository → Protected branches). У форка обычно уже так.
3. Собственный CI-файл ревьюера
В форке: Settings → CI/CD → General pipelines → CI/CD configuration file =
ci/build-image.yml→ Save.
Корневой .gitlab-ci.yml служит шаблоном, который будут подключать ваши проекты, а не пайплайном самого ревьюера.
4. Trigger token и запрет pipeline-переменных
В форке: Settings → CI/CD → Pipeline trigger tokens → Add new token (описание
ai-review). Создавать от аккаунта, который может запускать пайплайны на защищённойmain(Maintainer/Owner). ЭтоREVIEWER_TRIGGER_TOKEN.Settings → CI/CD → Variables → Minimum role to use pipeline variables = No one allowed. На gitlab.com это обычно уже так по умолчанию, но проверьте. Иначе любой разработчик с trigger-токеном сможет подменить образ и запустить его с ключами моделей.
5. Разрешить проверяемым проектам тянуть образ
В форке: Settings → CI/CD → Job token permissions → Authorized groups and projects → Add → группа
my-groupцеликом (или каждый проверяемый проект по отдельности).
6. Переменные проекта-ревьюера
В форке: Settings → CI/CD → Variables. Каждая должна быть Masked и Protected.
GITLAB_BOT_TOKEN:api-токен бота из шага 1.REVIEWER_OPENAI_API_KEY: ключ OpenAI (маршрут по умолчанию).По желанию, для других моделей:
REVIEWER_DEFAULT_MODEL:openai(по умолчанию),opus,deepseek,qwen,gemini,grok;REVIEWER_ANTHROPIC_API_KEY: дляai::opusи для Draft/WIP MR;REVIEWER_DEEPSEEK_API_KEY,REVIEWER_GEMINI_API_KEY,REVIEWER_XAI_API_KEY,REVIEWER_QWEN_API_KEY;REVIEWER_SECOND_OPINION: маршрут для второго, «совещательного» ревью без голоса (удваивает стоимость, включать осознанно).
По желанию:
REVIEWER_FORBID_CLOSING_REFERENCES=true, если в MR нельзя писатьCloses #N(задача закрывается вручную после проверки на проде).
Подходят только имена с префиксом REVIEWER_: OPENAI_API_KEY и т.п. без префикса не читаются.
7. Собрать образ
В форке: Build → Pipelines → Run pipeline на
main.В пайплайне нажать ▶ на ручном job
build-image, дождаться успеха.Deploy → Container registry: должен появиться образ
registry.gitlab.com/my-group/code-review:latest.
Пересобирать после каждого Update fork.
8. Переменные группы
Group my-group → Settings → CI/CD → Variables. Все НЕ Protected (MR-пайплайны идут на незащищённых ветках). Маскировать только токены:
REVIEWER_TRIGGER_TOKEN: trigger token из шага 4. Masked.REVIEWER_SOURCE_PROJECT=my-group/code-review. Visible, не маскировать.Только Free:
REVIEWER_BOT_USER_ID= id бота из шага 1 (обязательно). Visible, не маскировать.Только Free:
REVIEWER_READ_TOKEN=read_api-PAT бота из шага 1. Masked.
Путь и id не секретны, а GitLab заменяет на [MASKED] любое вхождение замаскированного значения в логе: пропадут ссылки на пайплайн и job, имя образа, а цифры id вообще везде, где встретятся. Переменную Masked and hidden размаскировать нельзя, только удалить и создать заново.
Здесь не должно быть GITLAB_BOT_TOKEN и ключей моделей: они живут только в форке.
9. Подключить шаблон в каждом проверяемом проекте
В .gitlab-ci.yml каждого проекта (создать файл, если CI ещё нет):
stages: # ...существующие стадии проекта, если есть... - review # This section is required for AI code review include: - project: my-group/code-review ref: main file: /.gitlab-ci.yml ai-review: stage: review verify-approvals: stage: review follower-review: stage: review
Явная стадия review нужна: если в пайплайне только jobs из .post, он может оказаться skipped и не удовлетворит «Pipelines must succeed».
Если раннеры с тегами, добавить в тот же файл:
.ai-reviewer: tags: [my-runner-tag]
10. Read-токен для merge gate
Free: ничего не делать, это групповая переменная REVIEWER_READ_TOKEN из шага 8. Компромисс: любой разработчик группы с правом пуша может прочитать этот токен и через него все проекты группы. Для маленькой команды, где все и так видят всё, это нормально.
Premium/Ultimate. Отдельный токен на каждый проект, локально в клоне форка:
git clone git@gitlab.com:my-group/code-review.git && cd code-review make install export GITLAB_SERVER_URL=https://gitlab.com export GITLAB_ADMIN_TOKEN=<ваш PAT, scope api, Maintainer+ на проектах> make tokens-list GROUP=my-group > projects.txt # удалить из projects.txt проекты, которые ревьювить не нужно make tokens-grant make tokens-audit # проверить, что у всех проектов есть токен
projects.txt сохраните (он в .gitignore): пригодится для make tokens-audit / make tokens-rotate.
11. Включить блокировку merge
В каждом проверяемом проекте:
Settings → Merge requests → Merge checks → Pipelines must succeed ✔.
Без этого verify-approvals упадёт, но кнопка Merge останется активной.
Желательно также: Settings → Repository → Protected branches → main: Allowed to push = No one. Тогда код попадает в main только через MR.
12. Проверка на тестовом MR
Создать issue в проекте, например
#1.Из issue нажать Create branch (получится
1-short-description) или назвать ветку так вручную: номер issue в начале.Сделать маленькое изменение, открыть MR, в описании
Related to #1(илиCloses #1).ai-reviewзавершается за секунды; в его логе ссылка на пайплайнrun-reviewв форке: открыть её и следить.run-reviewоставляет комментарий в MR и ставит 👍 (APPROVED) или 👎 (REJECTED) от имени бота.verify-approvalsна первом прогоне может упасть, это нормально: голосов ещё нет.Поставить 👍 с аккаунта, который не автор MR, и перезапустить
verify-approvals→ должен пройти, если AI одобрил.
Для соло-разработки (вы автор, второго человека нет): выставить REVIEWER_ALLOW_AUTHOR_APPROVAL=true (и при желании REVIEWER_HUMANS_WHEN_AI_OBJECTS=1), иначе ваш собственный 👍 не засчитается. Ставить в переменные группы (как в шаге 8), а не в форк: голоса считает verify-approvals, а он работает в пайплайне проверяемого проекта.
Если что-то не так, смотрите таблицу «Where failures usually come from» в README.md.
13. Повседневная работа
Ветки:
<номер issue>-описание(1085-skip-login), одинаково во всех репозиториях, которые затрагивает задача.Задача на несколько репозиториев: коммиты в соседних проектах должны ссылаться на issue (
my-group/service-a#1085в сообщении коммита), так ревьюер найдёт все ветки задачи. MR открывать только в проекте, где лежит issue; в остальных ревьюер сам создаст MR с меткойmerge::follower, их мёржить в нужном порядке по одной кнопке.Модель на конкретный MR выбирается меткой:
ai::opus,ai::openai,ai::deepseek,ai::qwen,ai::gemini,ai::grok(создать эти метки в группе: Group → Manage → Labels). ЗаголовокDraft:/WIP:даёт быстрый дешёвый прогон.Правила для ревьюера класть в сами репозитории:
CLAUDE.md,AGENTS.md,CONTRIBUTING.md,ARCHITECTURE.md,code-review-rules.md,docs/. Он читает их как требования.Регулярно: Update fork → пересобрать образ (шаг 7); следить за сроком PAT бота (Free) и ротировать заранее.
Итого
Полноценное AI ревью по задаче оказалось недостающим звеном: самые дорогие баги в микросервисах живут на стыках, и именно они не видны ни человеку, открывшему один MR, ни AI, получившему один diff. Агент с полным рабочим пространством всех репозиториев задачи, правилами каждого из них и описанием issue видит картину целиком, а кворум «2 из 3» оставляет последнее слово людям.
Уволить себя у меня так и не вышло. Вышло другое: ушла та часть работы, которую я и раньше делал без удовольствия. Освободившееся время уходит на задачи, до которых раньше не доходили руки. Прям как в субботу утром: пока кто-то стоит в пробке, у меня пустая дорога.
Репозиторий: https://gitlab.com/nick-public/code-review. Issues и MR приветствуются.

