AI написал код и тесты, всё зелёное — почему задача всё ещё не решена
Coding Agent написал код и 20 тестов, все зелёные, — а независимая приёмка по требованиям прошла 4 из 10. Разбираем на реальном эксперименте, почему тесты автора закрепляют его догадки, как устроить независимую приёмку и ревью и почему отчёт агента — не доказательство.
Владимир Павлунин · опубликовано
Короткий ответ
Можно ли считать задачу решённой, если Coding Agent написал код и тесты и все тесты зелёные?
Нет. Если агент сам написал и код, и тесты, зелёные тесты означают только одно: автор согласен сам с собой. Тесты проверяют код против понимания задачи автором, и если он придумал цену или правило, тест закрепит именно выдумку. В моём эксперименте собственные тесты агента были зелёными во всех 6 прогонах, а независимая приёмка по требованиям дала от 2 до 4 из 10 без контекста и 10 из 10 с контекстом. Доказательством служит проверка, которая не зависит от автора: приёмочные тесты из требований, ревью со свежим контекстом и проверки в конвейере, а не отчёт агента.
Главное
- Агент, который пишет и код, и тесты, проверяет себя сам: в прогоне без контекста тест прямо утверждал, что самовывоз бесплатный, — то есть закреплял цену, которую агент придумал.
- Собственные тесты агента были зелёными в 6 прогонах из 6. Независимая приёмка по требованиям: 2–4 из 10 без контекста, 10 из 10 с контекстом.
- Независимость — это другой источник знаний, а не другая модель: валидатор писал тесты по требованиям, контракту и ADR, а не по коду.
- Ревьюер с пакетом контекста за 81 секунду и 25 центов нашёл 3 блокера и ошибку, которую тесты автора маскировали: неверное правило возврата при нулевой цене доставки.
- Отчёт агента — это утверждение, а не доказательство. Доказательство — результат проверки, которую запустила система.
- Когда код пишет агент, ревью человека смещается к намерению: правильные ли требования, критерии приёмки и риски.
Coding Agent написал рабочий код меньше чем за 5 минут. 20 тестов, все зелёные. Отчёт аккуратный: что изменено, почему, какие крайние случаи покрыты. И при этом задача не решена: цена не та, поля API не те, флаг выкатки отсутствует, правило возврата неверное.
Это прогон A2 из моего эксперимента для выпуска «От Coding Agent к AI Development Platform». Ниже — почему зелёные тесты агента ничего не доказывают о правильности решения и как устроить проверку, которой можно верить.
Короткий ответ
Если агент пишет и код, и тесты к нему, тесты проверяют код против понимания задачи самим агентом. Когда понимание неверное, тест не ловит ошибку, а закрепляет её. Зелёный цвет означает «автор согласен сам с собой».
Доказательством может быть только проверка, которая не зависит от автора:
- приёмочные тесты, написанные по требованиям, а не по коду;
- ревью со свежим взглядом и доступом к требованиям;
- проверки в конвейере: CI, архитектурные правила, сканеры безопасности.
Отчёт агента «всё проверено» к доказательствам не относится.
Контекст эксперимента
Коротко, подробности — в разборе эксперимента A. Синтетический сервис доставки shipline, 9 исходных тестов. Задача: добавить самовывоз из пунктов выдачи партнёра. В тикете нет цены, лимитов, названий полей API, правил выкатки и возврата — всё это есть в требованиях, контракте и ADR, которые агент в условии A не видел.
6 прогонов одной модели (claude-sonnet-5 в Claude Code): 3 в условии A (задача и репозиторий) и 3 в условии B (плюс 8 документов контекста на 522 слова). Это демонстрация механизма, а не бенчмарк.
Как тест закрепляет выдумку
В прогоне A2 агент решил, что самовывоз бесплатный. Он нигде этого не прочитал — так получилось. Дальше он честно написал тесты на своё решение:
def test_pickup_available_when_point_in_city():
method = SelfPickup(InMemoryPickupPointRepository([MOSCOW_PICKUP_POINT]))
quote = method.quote(make_order())
assert quote is not None
assert quote.price == Money(0)
И на уровне API:
assert pickup["price"] == {"amount": 0, "currency": "RUB"}
assert pickup["points"] == [{"id": "pvz-1", "address": "Арбат, 10", "hours": "10:00-22:00"}]
Первый тест закрепляет цену, которую агент придумал. Второй вдобавок закрепляет имена полей points и id, хотя контракт с мобильным и веб-клиентом требует pickup_points и pickup_point_id. Если завтра кто-то приведёт код в соответствие с требованиями, эти тесты покраснеют. Тесты защищают не требования, а ошибку.
В других прогонах то же самое с правилом возврата. По требованиям стоимость доставки при возврате из пункта выдачи не возвращается. Агент в условии A во всех 3 прогонах решил иначе и написал на это тест:
a1 test_partner_pickup_fee_refunded: refund == 1199 ₽ (товары 1000 + доставка 199)
a3 test_pickup_point_fee_refunded: refund == 1150 ₽ (товары 1000 + доставка 150)
Имя теста прямо говорит, что он проверяет: «стоимость доставки возвращается». Это тест неправильного правила.
И самый тонкий случай — снова A2:
def test_pickup_point_fee_refunded():
order = make_order()
order.delivery = DeliverySelection("pickup_point", Money(0), 2, {"point_id": "pvz-1"})
assert refund_amount(order) == Money.rub(1000)
Тест называется «доставка возвращается», но доставка стоит 0, поэтому ответ 1000 рублей получается при любом правиле. Тест не может различить правильное и неправильное поведение. Ошибка в правиле возврата замаскирована ошибкой в цене: как только цену исправят на 99 рублей, компания начнёт возвращать деньги за доставку, и ни один тест этого не заметит.
Это не про ИИ. Человек, который пишет тесты к своему коду по своему пониманию задачи, попадает в ту же ловушку. Просто агент делает это быстрее и в большем объёме.
ПОНИМАНИЕ ЗАДАЧИ АВТОРОМ
(цена 0 ₽, поле points,
доставка возвращается)
↙ ↘
КОД ТЕСТЫ
↘ ↙
ЗЕЛЁНЫЙ ЦВЕТ
= код согласован с пониманием автора
≠ код соответствует требованиям
Независимая приёмка: 2–4 из 10 против 10 из 10
Нужна проверка, которая не зависит от автора. Я запустил отдельного агента-валидатора со свежим контекстом. Что у него было и чего не было:
ВИДИТ НЕ ВИДИТ / НЕ МОЖЕТ
требования PRD-pickup.md как писался код
контракт delivery-options.openapi.yaml тесты автора как доказательство
ADR-011 о выкатке менять shipline/ и tests/
результат прогона через публичный API запускать что-то кроме pytest и ls
Задание — 10 проверок, по одной на пункт: 6 бизнес-правил из требований (цена и порог, срок, вес, наличие активного пункта, обязательный выбор пункта, правило возврата), 3 проверки контракта (код способа, формат списка пунктов, коды ошибок 422) и 1 проверка ADR-011 (флаг выключен по умолчанию). Валидатор пишет приёмочные тесты pytest в отдельный каталог и возвращает JSON с результатом и одной строкой доказательства на проверку.
Результат:
тесты автора приёмка валидатора
a1 22 из 22 зелёные 2 из 10
a2 20 из 20 зелёные 4 из 10
a3 19 из 19 зелёные 4 из 10
b1 26 из 26 зелёные 10 из 10
b2 24 из 24 зелёные 10 из 10
b3 26 из 26 зелёные 10 из 10
Собственные тесты агента зелёные везде. А независимая приёмка в условии A находит от 6 до 8 расхождений из 10.
Во всех 3 прогонах A валидатор провалил одни и те же 6 пунктов: цену, общий лимит веса 15 кг, правило возврата, формат списка пунктов, коды ошибок и флаг выкатки. Вот как выглядит его проверка возврата для A2:
def test_prd6_delivery_fee_not_refunded_for_pickup_point():
order = make_order(price_rub=1000)
order.delivery = DeliverySelection("pickup_point", Money.rub(99), 2, {"pickup_point_id": "pvz-1"})
assert refund_amount(order) == Money.rub(1000)
Разница с тестом автора — одно число. Валидатор взял стоимость доставки из требований, 99 рублей, а не из реализации. И замаскированная ошибка сразу видна: refund_amount возвращает 1099 рублей вместо 1000.
А так выглядит проверка флага в прогоне B1, где всё прошло:
def test_adr011_disabled_by_default(api, order_repo):
assert config.FLAGS.get(FLAG_NAME) is False
order_repo.save(make_order("o1", MOSCOW, price_rub=1000, weight_g=1000))
_, body = api.get_delivery_options({"order_id": "o1"})
assert find_option(body, "pickup_point") is None
status, body = api.post_select_delivery(
{"order_id": "o1", "method": "pickup_point", "pickup_point_id": "pp-1"}
)
assert status == 422
assert body["error"] == "delivery_not_available"
По умолчанию флаг выключен: самовывоза в вариантах нет, а попытка его выбрать возвращает ошибку. Такие тесты описывают поведение, которое нужно бизнесу, и не зависят от того, как именно написан код.
Главное здесь — откуда валидатор берёт знание. Независимость не в том, что это другой агент, а в том, что у него другой источник истины. Если бы валидатор тоже смотрел только в код, у него не было бы, из чего взять 99 рублей: единственная цена самовывоза в репозитории — та, что придумал автор.
Ревью со свежим контекстом за 25 центов
Второй независимый взгляд — ревью. Я дал другому агенту diff прогона A2 и тот же пакет контекста, что был в условии B: требования, контракт, ADR, правила, политику безопасности и разбор инцидента. Инструменты — только чтение и git diff, без права что-либо менять.
Результат за 81 секунду и 0,25 доллара — 7 замечаний:
BLOCKER цена захардкожена: 0 ₽ вместо 99 ₽ / бесплатно от 3000 ₽ (PRD п.1)
BLOCKER способ зарегистрирован без флага, включён у всех сразу (ADR-011)
BLOCKER поля points / id / point_id вместо контрактных (контракт API)
MAJOR не проверяется общий лимит 15 кг (PRD п.3)
MAJOR доставка возвращается; баг замаскирован нулевой ценой (PRD п.6, INC-2025-031)
MAJOR обе ошибки выбора сворачиваются в delivery_not_available (контракт API)
MINOR комментарий про PostgreSQL, а реализован только SQLite
Вердикт: мержить нельзя — минимум 3 блокера.
Пятый пункт я сам пропустил, когда разбирал прогоны: ревьюер заметил, что ошибка в правиле возврата сейчас не видна, потому что доставка бесплатная, и проявится в момент исправления цены. Это ровно тот сценарий, который описывает инцидент INC-2025-031 из пакета контекста: новый способ доставки с неверным правилом возврата.
Та же модель, что писала код. Разница — только в контексте и в вопросе к коду.
Отчёт агента — не доказательство
Ещё один урок из тех же прогонов. В прогоне B2 агент 16 раз пытался запустить тесты командой, которую правила доступа не разрешали. В какой-то момент он решил поручить проверку субагенту — «возможно, у него другие права». Субагент получил те же правила, его команды тоже отклонили. Подробно эта история разобрана в статье про права агента.
Чем закончилось: агент так и не запустил тесты и написал в отчёте, что уверен в логике по ручной трассировке кода, и попросил человека запустить pytest. Тесты при этом проходят — я запустил их отдельно, 24 из 24.
Здесь агент повёл себя честно: не заявил, что тесты прошли. Но представьте тот же отчёт без последней фразы. «Реализовал, покрыл тестами все правила, уверен в логике» — звучит как готовая задача.
И обратный случай — A2. Его отчёт заканчивается фразой «All 20 tests pass», и это правда. Но она ничего не говорит о том, решена ли задача.
ЧТО ГОВОРИТ АГЕНТ ЧТО ЭТО ДОКАЗЫВАЕТ
«все 20 тестов проходят» код согласован с тестами автора
«уверен в логике» ничего
«покрыл все правила требований» ничего, пока не видно требований
ЧТО ЗАПУСТИЛА СИСТЕМА ЧТО ЭТО ДОКАЗЫВАЕТ
CI с тестами автора нет регрессий и падений
приёмка по требованиям поведение соответствует требованиям
архитектурные правила, сканеры нет известных нарушений правил
Мы не принимаем задачу у человека со словами «я всё проверил». Мы смотрим на зелёный конвейер. С агентом так же.
Три роли вместо одной
Получается 3 роли, и у каждой своя причина существования:
ТРЕБОВАНИЯ · КОНТРАКТ · ADR
↓ ↓ ↓
АВТОР РЕВЬЮЕР ВАЛИДАТОР
пишет код критика со приёмочные
и свои свежим тесты из
тесты контекстом требований
↘ ↓ ↙
КОНВЕЙЕР (CI)
тесты · приёмка · архитектурные
правила · секреты · зависимости · SAST
↓
РЕШЕНИЕ О МЕРЖЕ — ПО РЕЗУЛЬТАТАМ,
А НЕ ПО ОТЧЁТУ АВТОРА
Это не многоагентная архитектура ради красоты. Разные роли нужны, потому что у них разный контекст и разные вопросы к коду: автор спрашивает «работает ли то, что я написал», ревьюер — «соответствует ли изменение правилам», валидатор — «делает ли система то, что нужно бизнесу».
Отдельно — безопасность. Veracode в 2025 году прогнал больше 100 моделей на 80 задачах, где можно написать код безопасно или небезопасно, и в 45% случаев код не прошёл проверку безопасности. Задачи подобраны специально, так что это не доля уязвимостей в реальных репозиториях. Вывод из этого не «ИИ пишет дырявый код», а «проверка безопасности должна быть в конвейере, а не в надежде на модель». Шлюз на практике состоит из понятных вещей: поиск секретов в изменениях, проверка новых зависимостей, статический анализ и проверки правил вашей архитектуры. В эксперименте пример последнего — детерминированная проверка «нет сетевых вызовов в пути запроса»: это правило компании, а не общий стандарт, и проверять его должен конвейер.
Сколько это стоит
роль время стоимость за прогон
автор, условие A 262–434 c $0,80–1,13
автор, условие B 275–547 c $1,03–1,85
валидатор 173–233 c $0,43–0,68
ревьюер (прогон A2) 81 c $0,25
Сравните с ценой ошибки: бесплатная доставка в продакшене или месяцы возвратов за доставку, которые компания возвращать не собиралась. Лишние агенты ради красоты не нужны, но агент с другим контекстом и другим вопросом к коду окупается сразу.
Есть и обратная сторона. Каждая проверка стоит времени, а кода становится больше. Faros AI по телеметрии более 10 000 разработчиков сообщает, что в командах с высоким использованием ИИ время ревью pull request'ов выросло на 91%, а сами pull request'ы стали крупнее на 154% (вендорский отчёт, корреляция). DORA в 2025 году фиксирует, что связь использования ИИ со стабильностью поставки остаётся отрицательной там, где нет сильных систем контроля: автотестов, контроля версий, быстрой обратной связи. Поэтому независимые проверки должны быть автоматическими, а человек — смотреть туда, где автоматика бессильна. Почему ревью становится узким местом, разобрано в статье о том, почему AI не ускоряет разработку в 10 раз.
Куда смещается ревью человека
Если код пишет агент, а тесты по требованиям проверяет валидатор, самое ценное место для человека — не строчки кода, а намерение:
- правильные ли требования;
- правильные ли критерии приёмки;
- те ли риски мы проверяем.
Ошибку в требованиях не поймает никакой валидатор: он честно проверит неправильную цену. Поэтому ревью смещается влево — к задаче и критериям приёмки — и вправо — к рискованным изменениям, которые политика доступа отметила как требующие подтверждения.
Ограничения
- 6 прогонов, один синтетический репозиторий, одна модель; проверки и формулировку задания валидатору писал я.
- Валидатор — тоже модель. В разных прогонах он писал тесты по-разному, а для проверок функциональности сам подбирал способ передать данные о пунктах и включить флаг, если реализация их даёт. Его тесты нужно читать так же, как тесты автора.
- Приёмочные тесты в эксперименте писались после кода. В нормальном процессе их пишут до кода из требований, и тогда они становятся частью постановки.
- Цифры стоимости и времени — для небольшого репозитория и одной задачи, не норматив.
Что делать
- Не принимать изменение по тестам автора. Тесты агента — проверка регрессий, а не критерий приёмки.
- Писать приёмочные тесты из требований. Лучше до кода, хотя бы для самых частых и дорогих типов задач. Отдельный агент-валидатор с доступом только к требованиям — дешёвый способ начать.
- Давать ревьюеру контекст. Ревью diff без требований ловит стиль; ревью с требованиями, контрактом и ADR ловит решения. Как собрать такой пакет — в статье о context engineering.
- Верить конвейеру, а не отчёту. Решение о мерже — по результатам CI, приёмки и сканеров, которые запустила система.
- Сместить внимание людей к требованиям и рискам. Это единственное место, где ошибку не исправит ни автор, ни валидатор.
- Измерять. Сколько изменений возвращается с приёмки и ревью и сколько исправлений приходит после релиза — метрики, которые показывают, работает ли проверка. Какие ещё метрики нужны — в статье о метриках AI-разработки.
Независимая проверка — один из слоёв AI Development Platform: вместе с контекстом, политиками доступа и наблюдаемостью. Шаблоны критериев приёмки и чек-лист ревью AI-кода я готовлю в составе AI Development Kit. Если хотите посмотреть, как устроена проверка кода агентов в вашей команде, — начните с оценки.
Источники
- Эксперимент автора «От Coding Agent к AI Development Platform», 6 прогонов Claude Code (claude-sonnet-5), независимая приёмка и ревью на синтетическом репозитории shipline, 2026.
- 2025 GenAI Code Security Report. Veracode, 2025.
- The AI Productivity Paradox Report 2025. Faros AI, 2025.
- Announcing the 2025 DORA Report: State of AI-assisted Software Development. Google Cloud / DORA, 2025.
Вопросы и ответы
Если тесты зелёные, значит ли это, что код работает?
Это значит, что код делает то, что ожидал автор тестов. Если тесты писал тот же агент, что и код, они проверяют согласованность кода с его собственным пониманием задачи. Ошибку в понимании — неверную цену, чужие имена полей, неправильное правило возврата — такие тесты не поймают, а закрепят.
Чем приёмочный тест отличается от обычного теста агента?
Источником знаний и автором. Приёмочный тест пишется по требованиям, контракту и решениям архитектуры, обращается к сервису только через публичный API и не зависит от того, как устроен код. В идеальном процессе его пишут до кода, и тогда агент-автор получает не только задачу, но и способ проверить, что он её решил.
Разве можно доверять тестам, которые тоже написал агент?
Можно доверять больше, если у агента-валидатора другой контекст и другая задача: он не видел, как писался код, получил только требования и не может менять код. Но это всё ещё модель, и её тесты тоже нужно читать. Самое важное — откуда валидатор берёт знание: если бы он смотрел только в код, он бы подтвердил бесплатную доставку.
Сколько стоит независимая проверка?
В эксперименте валидатор стоил от 0,43 до 0,68 доллара и работал около 3–4 минут на прогон, ревьюер — 0,25 доллара и 81 секунду. Для сравнения, сами прогоны агента-автора стоили от 0,80 до 1,85 доллара. Это цифры одного небольшого синтетического репозитория, а не норматив.
Агент написал в отчёте, что всё проверил. Этого недостаточно?
Недостаточно. В одном из прогонов агент не смог запустить тесты из-за правил доступа и честно написал, что уверен в логике по ручной трассировке, и попросил человека запустить тесты. Это хорошее поведение, но уверенность — не результат. Мы не принимаем задачу у человека со словами «я всё проверил», мы смотрим на зелёный конвейер.
Что остаётся ревьюеру-человеку, если проверки автоматизированы?
Намерение. Правильные ли требования, правильные ли критерии приёмки, те ли риски проверяются. Ошибку в требованиях не поймает никакой валидатор: он честно проверит неправильную цену. Плюс изменения, которые политика доступа отметила как рискованные.
Нужно ли проверять безопасность кода от AI отдельно?
Да, как и любого кода, но в конвейере, а не в надежде на модель. Veracode в 2025 году на наборе из 80 задач, где можно выбрать безопасную или небезопасную реализацию, получил провал проверок безопасности в 45% случаев. Это не доля уязвимостей в реальном коде, а аргумент в пользу поиска секретов, проверки зависимостей, статического анализа и архитектурных правил в каждом изменении.