К содержанию
Владимир Павлунин

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 прогонов, один синтетический репозиторий, одна модель; проверки и формулировку задания валидатору писал я.
  • Валидатор — тоже модель. В разных прогонах он писал тесты по-разному, а для проверок функциональности сам подбирал способ передать данные о пунктах и включить флаг, если реализация их даёт. Его тесты нужно читать так же, как тесты автора.
  • Приёмочные тесты в эксперименте писались после кода. В нормальном процессе их пишут до кода из требований, и тогда они становятся частью постановки.
  • Цифры стоимости и времени — для небольшого репозитория и одной задачи, не норматив.

Что делать

  1. Не принимать изменение по тестам автора. Тесты агента — проверка регрессий, а не критерий приёмки.
  2. Писать приёмочные тесты из требований. Лучше до кода, хотя бы для самых частых и дорогих типов задач. Отдельный агент-валидатор с доступом только к требованиям — дешёвый способ начать.
  3. Давать ревьюеру контекст. Ревью diff без требований ловит стиль; ревью с требованиями, контрактом и ADR ловит решения. Как собрать такой пакет — в статье о context engineering.
  4. Верить конвейеру, а не отчёту. Решение о мерже — по результатам CI, приёмки и сканеров, которые запустила система.
  5. Сместить внимание людей к требованиям и рискам. Это единственное место, где ошибку не исправит ни автор, ни валидатор.
  6. Измерять. Сколько изменений возвращается с приёмки и ревью и сколько исправлений приходит после релиза — метрики, которые показывают, работает ли проверка. Какие ещё метрики нужны — в статье о метриках 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% случаев. Это не доля уязвимостей в реальном коде, а аргумент в пользу поиска секретов, проверки зависимостей, статического анализа и архитектурных правил в каждом изменении.

Читать дальше

СтатьяCoding Agents Почему Coding Agent мало одного репозитория: 3 прогона — 3 разные цены Разбор эксперимента: Coding Agent получил задачу и репозиторий и 3 раза написал аккуратный код с зелёными тестами — и каждый раз сам придумал цену, поля API, флаг выкатки и правило возврата. Почему это не ошибка кода и чего агенту не хватает. 3 октября 2026 Читать → СтатьяAI Development От Coding Agent к AI Development Platform: почему быстро написать код недостаточно Эксперимент на 6 прогонах: агент пишет код за минуты, но без контекста, прав и проверок решает не ту задачу. Из чего состоит AI Development Platform. 1 октября 2026 Читать → СтатьяAI Development Почему AI не ускоряет разработку в 10 раз: bottleneck просто переезжает в другое место Почему код в 10 раз быстрее не ускоряет поставку в 10 раз. Узкое место переезжает в ревью, тесты и требования. Закон Литтла, модель очередей, DORA 2024–2025 и METR с оговорками. обновлено 15 октября 2026 Читать → СтатьяAI Development Почему Coding Agent нельзя просто дать Full Access: архитектура Allow / Ask / Deny Как безопасно давать Coding Agent доступ к файлам, shell, сети и Git: Allow / Ask / Deny, policy engine по пользователю, задаче, действию, ресурсу и среде, sandbox, усталость от подтверждений, идентичность агента и аудит. С реальным демо трёх правил в Claude Code и историей прогона, где агент искал другие права через субагента. обновлено 11 октября 2026 Читать →