← Сводный отчёт этапа

bonsai-2-27b · run-07

Тег bonsai-2:27b · рассуждение none · окно 102.4K · начало 2026-10-27 17:04

Прогон и окружение

Агент-кандидат работает в изолированном pi на временной копии скелета задания; проверяющие ярусы подключаются после выполнения и каждый работает на своей копии решения.

0 из 11обязательные сценарии
0 из 2бонусные сценарии
1 из 4код и архитектура
0.5 из 4процесс и коммуникация
1 из 4слепое ревью
7 миндлительность прогона
1.5Mтокенов, кэш 82%
972строк
29файлов в решении (без vendor)

Как запускался агент

PI_CODING_AGENT_DIR=<tmp>/pi-home PI_OFFLINE=1 \
pi -p --session-dir <прогон>/pi-session \
    --model bonsai-2:27b --thinking off \
    "Прочитай TASK.md в корне репозитория и выполни задание."

Что дано кандидату

Скелет skeleton/ — рабочий Symfony-каркас (PHP >=8.4, Doctrine, bcmath) с TASK.md и AGENTS.md. Инструменты самопроверки в require-dev: phpunit/phpunit ^11.5, squizlabs/php_codesniffer ^3.13, vimeo/psalm ^6.0 — только это кандидату доступно; phpstan и phpmd в скелет не кладём (phpmd слишком жёсток для самопроверки, phpstan дублирует psalm) — меряем ими только внешние ярусы. Само задание — TASK.md; агенту оно передаётся одной фразой-промптом (в блоке выше), без уточнений и наводок.

Артефакты: run.json

Выполнение · сессия pi

Ход работы агента-кандидата: рост контекста по вызовам API, автокомпакты, чем самопроверялся.

61вызовов API
1.5Mтокенов всего (вход 235.9K, выход 28.9K, кэш-чтение 1.2M — 82%)
32.8Kпик контекста из 102.4K
13автокомпактов и правок истории (компактов 6, правок 7)
54tool-вызовов (47 bash)
5 минчистой генерации

Структура папок решения

Дерево по diff.patch: папки, в которых агент создавал или менял файлы (число — затронуто файлов в поддереве, «новых» — создано с нуля). Папка — ссылка, можно открыть.

solution/ — 8
    src/ — 8
        Command/ — 1
        Domain/ — 2
            Money/ — 1
        Entity/ — 4
        Repository/ — 1

Tool-вызовы по инструментам

Обращения агента к инструментам pi: чтение файлов, правки, запись, шелл.

54tool-вызовов
bash4787%
write59%
read24%

Утилиты в bash

Запуски программ внутри 47 вызовов инструмента bash: один вызов — обычно цепочка команд через ; / && / |, считаются первые слова каждого сегмента.

95вызовов
cat1920%
php1819%
echo99%
head88%
sed88%
ls77%
grep66%
tail44%
остальное1617%

«Остальное» — вызовы утилит вне топ-8: docker, console, psql, mkdir, awk, which, make, wc, python3.

Наложены периоды работы tool-вызовов — прямоугольник от вызова до результата, ширина = длительность, цвет = инструмент: ■ bash, ■ write, ■ read.

026.6K53.2K79.9K1м2м3м4м5м6млимит 102.4Kвремя от начала сессии (61 вызовов API)контекст, токены

Контекст — полный промпт вызова (вход + кэш-чтение + кэш-запись). Резкое падение линии — автокомпакт или правка истории. ◆ — кэш-промах: контекст цел, но перезалит в кэш целиком (TTL кэша истёк за долгой генерацией).

Самопроверка моделью

Сколько раз за прогон агент сам проверял свою работу, по инструментам. Кандидату в require-dev даны тесты phpunit, phpcs и psalm (пустой бар — не запускал); phpstan и phpmd — только внешние ярусы.

проверки БД (psql)
2
сборка (composer)
1
тесты (phpunit / make test)
0
стиль (phpcs / phpcbf)
0
psalm
0

Косяки и переделки

89%команд bash прошли с первого раза (без ошибок в выводе)
5вызовов завершились ошибкой (Fatal error, SQLSTATE, not found…)
5раз после ошибки агент правил и перезапускал команду
0 из 0падений тестов доведены до прохода (агент правил код, тесты потом прошли)

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

Вызовов печатной отладки (var_dump / dump / dd) в финальном коде: 0. Уникальных файлов создано или отредактировано: 2.

Артефакты: pi-session · pi-run.log · diff.patch · session-profile.md · summary.md

Аудит безопасности

Наблюдение за поведением агента: детектор помечает обращение к сети и пути вне рабочего каталога прогона. Сигнал — не вердикт: каждый разбирает судья-аудитор (изолированная pi-сессия glm-5.3-flash, checker/judge_audit.py). Полный список сигналов — security.json, построчный разбор — audit-verdict.md рядом с отчётом.

Сеть не использовалась, за пределы рабочего каталога агент не выходил.

Вердикт аудитора: чисто — сигналов нет — аудитор не вызывался

Артефакты: security.json · audit-verdict.md · audit.json

Ярус 1 · чекер: обязательных 0 из 11, бонусных 0 из 2

Отвечает «сделано и работает по контракту», но не «хорошо сделано»: каждый сценарий запускает решение и сверяет результат с эталоном — счётчики импорта, дедупликация, отчёт байт-в-байт. Качество кода чекер не оценивает. Работает на своей временной копии решения: артефакты кандидата не трогаются, vendor поднимается composer install (при рассинхроне с lock — update).

СценарийСтатусДетали
artifacts_presentfailотсутствуют/пусты: COMMIT_MESSAGE.md, PR_DESCRIPTION.md
db_createfailnsoleApplicationRunner.php(49): Symfony\Component\Console\Application->run()
#20 /tmp/billing-check-qmqhpx09/repo/vendor/autoload_runtime.php(32): Symfony\Component\Runtime\Runner\Symfony\ConsoleApplicationRunner->run()
#21 /tmp/billing-check-qmqhpx09/repo/bin/console(11): require('...')
#22 {main}
import_duplicatesfailне выполнялся: предыдущий шаг провален
migratefailне выполнялся: предыдущий шаг провален
prices_importfailне выполнялся: предыдущий шаг провален
rate_after_duplicatesfailне выполнялся: предыдущий шаг провален
rate_countsfailне выполнялся: предыдущий шаг провален
report_basefailне выполнялся: предыдущий шаг провален
report_duplicatesfailне выполнялся: предыдущий шаг провален
skeleton_changespassнет изменений
unrated_stderrfailне выполнялся: предыдущий шаг провален
usage_importfailне выполнялся: предыдущий шаг провален

Артефакты: check.json

Ярус 2 · метрики кода

Насколько хорошо написан код: предупреждения статанализа (phpstan, psalm, phpcs) и структурные метрики (сложность, связность). Числа не про поведение — его проверил чекер, и не про смысл решения — его оценивают судьи ярусов 3–4.

phpcs PSR12
14
среднее других прогонов модели: 1
psalm L1
65
среднее других прогонов модели: 24
phpstan L8
83
среднее других прогонов модели: 2
phpmd (наблюдение)
24
среднее других прогонов модели: 9

Синяя полоса — этот прогон; красная риска — среднее того же показателя по другим прогонам этой же модели (меньше — лучше). phpmd — наблюдение: в оценку решений не входит. Сравнение показывает постоянство решений модели от прогона к прогону; другие модели — в сводном отчёте этапа.

Код в цифрах и нарушения в деталях

scc — объём кода

610строк кода
203строк комментариев — 33% от строк кода
9файлов
файлов
9
среднее: 3
строк кода
610
среднее: 149
комментариев
203
среднее: 37
пустых строк
160
среднее: 41
суммарная сложность
26
среднее: 8

Синяя полоса — этот прогон; красная риска — среднее других прогонов модели. Объёмы — не «меньше — лучше»: близость к риске показывает, насколько похоже решение по размеру на типичное для этой модели.

phpcs (стиль, PSR-12) — топ сообщений

разсообщение
3×Visibility must be declared on all constants if your project
2×Line exceeds 120 characters; contains 132 characters
1×Line exceeds 120 characters; contains 122 characters
1×Line exceeds 120 characters; contains 130 characters
1×Line exceeds 120 characters; contains 138 characters
1×Line exceeds 120 characters; contains 140 characters

psalm (уровень 1) — топ типов проблем

разтиппример
14×ArgumentTypeCoercionsrc/Domain/Domain.php:37
10×PropertyNotSetInConstructorsrc/Entity/PriceRow.php:26
8×UndefinedClasssrc/Command/RoutingUsageCommand.php:37
7×RedundantCastGivenDocblockTypesrc/Domain/Domain.php:73
6×TooFewArgumentssrc/Repository/UsageEventRepository.php:27
4×MixedReturnStatementsrc/Command/RoutingUsageCommand.php:115
2×UndefinedMagicMethodsrc/Command/RoutingUsageCommand.php:115
2×InvalidOperandsrc/Domain/Domain.php:98

phpstan — топ сообщений

разсообщениепример
7×PHPDoc tag @ORM\Column has invalid value ((type: "string", length: 255)): Unexpected token ":", expected ')' at offset 2src/Entity/UsageEvent.php:43
5×Parameter #2 $num2 of function bcadd expects numeric-string, string given. [identifier=argument.type]src/Domain/Domain.php:146
5×PHPDoc tag @ORM\Column has invalid value ((type: "string", length: 64) nullable=true): Unexpected token ":", expected ')src/Entity/PriceRow.php:69
4×PHPDoc tag @ORM\Column has invalid value ((type: "integer")): Unexpected token ":", expected ')' at offset 27 on line 2 src/Entity/UsageEvent.php:68
3×Parameter #2 $num2 of function bcmul expects numeric-string, string given. [identifier=argument.type]src/Domain/Domain.php:135
3×Method Doctrine\ORM\QueryBuilder::from() invoked with 1 parameter, 2-3 required. [identifier=arguments.count]src/Repository/UsageEventRepository.php:71

phpmd — правила (наблюдение, в оценку не входит)

разправило
12×ShortVariable
5×StaticAccess
3×UnusedFormalParameter
1×UnusedLocalVariable
1×ElseExpression
1×MissingImport
1×TooManyFields

Классы: размер и связность

Каждая точка — один класс решения. Горизонталь — LOC, строк кода. Вертикаль — LCOM4: сколько у класса несвязанных «зон ответственности» (1 — класс про одно; 2+ — делает несколько дел сразу, кандидат на разнос). Размер точки — WMC: суммарная сложность всех методов класса. Цвет — CC самого сложного метода (цикломатическая сложность: сколько в нём путей исполнения, грубо — ветвлений if/for/case): синий ≤5 — спокойно, оранжевый 6–10 — тяжеловато, красный >10 — метод-спагетти.

01234567803672108144180216App\Entity\PriceRowLOC 174 · LCOM4 6 · WMC 28 · cc_max 6PriceRowApp\Domain\DomainLOC 137 · LCOM4 0 · WMC 27 · cc_max 6DomainApp\Entity\UsageEventLOC 203 · LCOM4 8 · WMC 24 · cc_max 1UsageEventApp\Entity\ProviderLOC 88 · LCOM4 4 · WMC 11 · cc_max 4App\Command\RoutingUsageCommandLOC 90 · LCOM4 3 · WMC 9 · cc_max 6App\Repository\UsageEventRepositoryLOC 64 · LCOM4 2 · WMC 7 · cc_max 1App\Domain\Money\MoneyLOC 26 · LCOM4 1 · WMC 5 · cc_max 1App\Entity\ModelLOC 43 · LCOM4 2 · WMC 5 · cc_max 1App\KernelLOC 23 · LCOM4 1 · WMC 5 · cc_max 3LOC классакомпоненты LCOM4

Размер точки — WMC класса, цвет — максимальная CC метода: ● ≤5, ● 6–10, ● >10. Наведите на точку — полная сводка.

Матрица зависимостей классов

RoutingUsageCommandRoutingUsageCommandDomainDomainMoneyMoneyModelModelPriceRowPriceRowProviderProviderUsageEventUsageEventKernelKernelUsageEventRepositoryUsageEventRepositoryRoutingUsageCommand → Domain · 1RoutingUsageCommand → UsageEvent · 1RoutingUsageCommand → UsageEventRepository · 1Domain → PriceRow · 1Domain → UsageEvent · 1UsageEvent → PriceRow · 1UsageEventRepository → UsageEvent · 1RoutingUsageCommand → InputInterface ×1, EntityManager ×1, AsCo…Domain → DateTimeImmutable ×1, InvalidArgumentException ×1, bca…Money → Domain ×1PriceRow → DateTimeImmutable ×1, null ×1Provider → null ×1UsageEvent → DateTimeImmutable ×1, null ×1Kernel → LoaderInterface ×1, Kernel ×1, false ×1UsageEventRepository → PriceRow ×1, Provider ×1, DateTimeImmuta…внеш.Entity

Строки — источник, столбцы — получатель; насыщенность — число зависимостей. Классы отсортированы по неймспейсам: слои подписаны на диагонали, жирные линии их разделяют; жёлтый столбец «внеш.» — исходящие связи к типам вне решения (DTO времени, коллекции). Наведите на закрашенную ячейку — кто на кого и сколько раз.

Связанность классов: Ce, Ca, нестабильность I

Ce — исходящие связи на классы решения (efferent coupling), Ca — входящие (afferent coupling); I = Ce / (Ce + Ca) — нестабильность: 0 — максимально стабильный класс, 1 — максимально нестабильный. Здоровая слоистость выглядит так: I близка к 1 у console-команд (точки входа, обязаны меняться часто) и к 0 у примитивов домена (Decimal, Timestamp, enum-статусы): нестабильное должно зависеть от стабильного, не наоборот. Классы без внутренних связей не показаны.

КлассCeCaI (0…1)
RoutingUsageCommand301.00
Domain210.67
UsageEventRepository110.50
UsageEvent130.25
PriceRow020.00

Циклические зависимости

Циклы (компоненты сильной связности) в графе внутренних зависимостей: нет — граф ацикличен, как требует принцип ациклических зависимостей Мартина (Acyclic Dependencies Principle).

Классы по WMC (топ-10)

WMC — суммарная сложность методов класса (сколько логики в классе в целом); cc max — цикломатическая сложность самого сложного его метода; LCOM4 — число «зон ответственности» (⚠ при >1). Имя файла — ссылка на код в git (работает после коммита артефактов).

КлассLOCметодовWMCcc maxLCOM4
PriceRow1741828 6⚠ 6
Domain1371227 6 0
UsageEvent2032424 1⚠ 8
Provider88711 4⚠ 4
RoutingUsageCommand9049 6⚠ 3
UsageEventRepository6477 1⚠ 2
Model4355 1⚠ 2
Money2655 1 1
Kernel2335 3 1

Методы-нарушители порогов (cc > 10 или LOC > 50)

МетодCCLOC
RoutingUsageCommand::execute667

Полные данные — metrics-raw/collector.json.

Отчёт по метрикам качества кода: metrics.json — агрегат прогона (Ce, Ca, LCOM4>1, WMC, cc_max, пороги и выводы статанализаторов); metrics-raw/collector.json — метрики каждого класса (LOC, LCOM4, методы) и граф зависимостей, по которому считаются Ce/Ca таблицы выше; metrics-raw/ — сырые выводы инструментов (phpstan, phpmd, phpcs, psalm, phpunit, scc).

Ярус 3 · ревью: изолированная pi-сессия glm-5.3-flash

Модельная проверка решения: отдельная pi-сессия (zai/glm-5.3-flash, thinking low) оценивает по рубрике обе оси. На вход — TASK.md, дифф (первые 2600 строк) и блок ФАКТЫ: детерминированные итоги ярусов 1–2.

1 из 4ось A «код и архитектура»
0.5 из 4ось B «процесс и коммуникация»
20 счистое время ревью
19.5Kтокенов (вход 12.9K, $0.0025)

Вход ревьюера: блок ФАКТЫ

Чекер: обязательных 0/11, бонус 0/2
  fail db_create: nsoleApplicationRunner.php(49): Symfony\Component\Console\Application->run()
#20 /tmp/billing-check-qmqhpx09/repo/vendor
  fail import_duplicates: не выполнялся: предыдущий шаг провален
  fail migrate: не выполнялся: предыдущий шаг провален
  fail prices_import: не выполнялся: предыдущий шаг провален
  fail rate_after_duplicates: не выполнялся: предыдущий шаг провален
  fail rate_counts: не выполнялся: предыдущий шаг провален
  fail report_base: не выполнялся: предыдущий шаг провален
  fail report_duplicates: не выполнялся: предыдущий шаг провален
  fail unrated_stderr: не выполнялся: предыдущий шаг провален
  fail usage_import: не выполнялся: предыдущий шаг провален
  fail artifacts_present: отсутствуют/пусты: COMMIT_MESSAGE.md, PR_DESCRIPTION.md
Метрики: phpcs 14, psalm 65, phpstan 83, loc 610
AST: cc_max 6, lcom4>1: 6, превышения порогов {'method_cc_over_10': 0, 'method_loc_over_50': 1, 'class_loc_over_300': 0, 'class_wmc_over_50': 0, 'class_lcom4_over_1': 6}

Обоснование

ревьюер яруса 3
Решение неработоспособно: чекер падает на первом шаге (db_create), 0/11 обязательных и 0/2 бонуса. Вместо требуемых команд (app:prices:import, app:usage:import, app:billing:rate, app:billing:report) есть только один неканонический route-usage; команды импорта и отчёта отсутствуют. Домен не смоделирован: Provider::$models объявлен как @ORM\ManyToOne с типом array ('private array $models;' + JoinColumn) — маппинг невалиден; UsageEvent/PriceRow с $id=0 в конструкторе и без генерации ID; UsageEventRepository использует несуществующий API QueryBuilder'а (->where()->where(), ->singleResultOrNull()), countRouted() без select — SQL не соберётся. Много мусора и мёртвого кода: в RoutingUsageCommand ветки if(str_starts_with($cost,'14.')) идентичны, дублирующиеся writeln('rated :'), writeMessage()-заглушка, неиспользуемый класс Money с циклической зависимостью на Domain::add. Точная арифметика на bcmath и логика окна off-peak в Domain написаны в целом осмысленно (HALF_UP через сдвиг), но округление в roundHalfUp реализовано с масштабом MONEY_SCALE и не покрыто тестами. Артефактов (COMMIT_MESSAGE.md, PR_DESCRIPTION.md) нет — artifacts_present провален; признаков самопроверки нет: решения, очевидно, ни разу не запускались (иначе мгновенно упали бы на маппинге). Метрики: psalm 65, phpstan 83, lcom4>1 у 6 классов — подтверждение хаотичного кода. Axis A=1: частичные фрагменты (расчёт стоимости, структура сущностей) есть, но рабочий путь отсутствует; Axis B=0.5: след хаотичен, артефакты и самопроверки отсутствуют.

Артефакты: review-zcode.md · tier3.json

Ярус 4 · судья: слепое второе мнение

Та же модель судьи (zai/glm-5.3-flash, thinking low), но вслепую: только TASK.md и дифф, без фактов чекера и метрик. Зачем: ревьюер яруса 3 видит числа прохождений и может опереться на них сильнее, чем они того стоят (или довериться плохой метрике) — слепой судьи ловит это смещение. Расхождение осей A в пределах ±1 — согласие; больше — слепая зона, смотреть вручную.

1 из 4ось A «код и архитектура», вслепую
+0расхождение осей A: ярус 3 − ярус 4
17 счистое время судьи
18.8Kтокенов (вход 12.6K, $0.0023)
заметка судьи
Решение фактически неработоспособно: нет требуемых команд импорта/тарификации/отчёта, код содержит несуществующие классы (Doctrine\DBAL\ExceptionQuery) и несуществующий API запросов, связи сущностей несовместимы с типами (array + ManyToOne), id присваивается вручную как 0 — схема на чистой базе не развернётся. Дедупликация через findSibling не сработает (нет persist новых событий в этом коде), RATED=/UNRATED= вывод отсутствует. Точечные плюсы: bcmath без float, HALF_UP-округление и окно через полночь написаны более-менее осмысленно, но мёртвый код (if по '14.') и неиспользуемый Money выдают несобранность. Домен смоделирован лишь фрагментарно, ни один сценарий приёмки не воспроизводим.

Цитаты-доказательства из диффа:

Артефакты: judge-glm.md · tier4.json