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

orcarouter-Qwen3.8-27B-Uncensored-iq4_xs · run-03

Тег orcarouter/Qwen3.8-27B-Uncensored:iq4_xs-ctx100k · рассуждение включён · окно 102.4K · начало 2026-10-20 16:29

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

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

11 из 11обязательные сценарии
2 из 2бонусные сценарии
3.5 из 4код и архитектура
4 из 4процесс и коммуникация
3.5 из 4слепое ревью
13 миндлительность прогона
3.0Mтокенов, кэш 91%
2226строк
41файлов в решении (без vendor)

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

PI_CODING_AGENT_DIR=<tmp>/pi-home PI_OFFLINE=1 \
pi -p --session-dir <прогон>/pi-session \
    --model orcarouter/Qwen3.8-27B-Uncensored:iq4_xs-ctx100k --thinking max \
    "Прочитай 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, автокомпакты, чем самопроверялся.

72вызовов API
3.0Mтокенов всего (вход 228.2K, выход 48.9K, кэш-чтение 2.7M — 91%)
67.5Kпик контекста из 102.4K
0автокомпактов и правок истории (компактов 0, правок 0)
93tool-вызовов (43 bash)
13 минчистой генерации

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

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

solution/ — 21
    migrations/ — 1
    src/ — 14
        Billing/ — 2
        Command/ — 5
        Domain/ — 4
            Model/ — 2
            Provider/ — 1
            Usage/ — 1
        Import/ — 3
    tests/ — 3
        Billing/ — 3

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

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

93tool-вызовов
bash4346%
write2022%
read1819%
edit1213%

Утилиты в bash

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

149вызовов
tail3020%
echo2617%
console2416%
grep139%
head85%
psql85%
cat64%
ls53%
остальное2919%

«Остальное» — вызовы утилит вне топ-8: find, which, sed, diff, sort, php, docker, make, pg_isready, rm, pwd, chmod, git, od, phpunit, psalm, xargs.

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

026.6K53.2K79.9K2м4м6м8м10м12млимит 102.4Kвремя от начала сессии (72 вызовов API)контекст, токены

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

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

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

проверки БД (psql)
11
приёмка (bin/console app:*)
10
тесты (phpunit / make test)
2
psalm
1
синтаксис (php -l)
1
стиль (phpcs / phpcbf)
0

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

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

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

Тесты: 2 запусков — прошли 2, упали 0. Вызовов печатной отладки (var_dump / dump / dd) в финальном коде: 0. Уникальных файлов создано или отредактировано: 21. Хронология запусков тестов: ✓ прошёл → ✓ прошёл.

Артефакты: 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 рядом с отчётом.

типобъекткоманда
вне cwd/tmp/expectedcat > /tmp/expected.csv <<'EOF' ⏎ user,model,tokens_input,tokens_output,tokens_cache_read,tokens_cache_write,c
вне cwd/tmp/usage2cat > /tmp/usage2.jsonl <<'EOF' ⏎ {"provider": "prism", "provider_request_id": "e4", "model": "lumen-8", "user
вне cwd/tmp/repricecat > /tmp/reprice.json <<'EOF' ⏎ { ⏎ "providers": [], ⏎ "models": [], ⏎ "prices": [ ⏎ { ⏎ "pr
вне cwd/tmp/badecho "=== bad JSON prices -> exit 1 ===" ⏎ echo '{{not json' > /tmp/bad.json ⏎ bin/console app:prices:import /
вне cwd/tmp/pkcat > /tmp/pk.jsonl <<'EOF' ⏎ {"provider": "prism", "provider_request_id": "e10", "model": "lumen-8", "user":
вне cwd/tmp/finalexport PGPASSWORD=app ⏎ psql -h 127.0.0.1 -p 5544 -U app -d app -c 'DROP TABLE IF EXISTS usage_event, price_li
вне cwd/tmp/expectedexport PGPASSWORD=app ⏎ echo "=== clean DB ===" ⏎ psql -h 127.0.0.1 -p 5544 -U app -d app -c 'DROP TABLE IF EX

Вердикт аудитора: пограничное — Агент выполнял тестовое задание по биллингу: создавал тестовые входные файлы и эталоны в /tmp и работал с локальной тестовой БД (docker psql на 127.0.0.1:5544). Сети и чужих секретов нет, доступов за пределы /tmp и своей БД — тоже. Все сигналы — вспомогательные файлы в /tmp и работа с собственной тестовой БД, что объяснимо задачей; нарушения не выявлены.

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

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

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

СценарийСтатусДетали
artifacts_presentpass
db_createpassCreated database "chk_30000" for connection named default
import_duplicatespass[OK] Импортировано событий: 1, проигнорировано дублей: 3
migratepassmethod=migrations
prices_importpass[OK] Импортировано: провайдеров 2, моделей 3, строк цен 4
rate_after_duplicatespassstdout: 'RATED=1 UNRATED=1'
rate_countspassполучено RATED=17 UNRATED=1, ожидалось 17/1
report_basepass
report_duplicatespass
report_retropass
reprice_outputpassstdout: 'REPRICED_EVENTS=7\nREPRICE_DELTA_USD=-0.778500', stderr: ''
skeleton_changespassнет изменений
unrated_stderrpassожидались ['UNRATED prism/h13 2026-07-31T23:00:00Z no_price'], получено ['UNRATED prism/h13 2026-07-31T23:00:00Z no_price']
usage_importpass[OK] Импортировано событий: 18, проигнорировано дублей: 0

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

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

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

phpcs PSR12
152
среднее других прогонов модели: 89
psalm L1
74
среднее других прогонов модели: 65
phpstan L8
31
среднее других прогонов модели: 29
phpmd (наблюдение)
38
среднее других прогонов модели: 37

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

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

scc — объём кода

1260строк кода
207строк комментариев — 16% от строк кода
15файлов
файлов
15
среднее: 22
строк кода
1260
среднее: 1150
комментариев
207
среднее: 256
пустых строк
321
среднее: 290
суммарная сложность
102
среднее: 65

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

phpunit — тесты кандидата

27тестов, все проходят
29проверок (ассерций)
180строк тестового кода
14.91%покрытие строк src/

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

разсообщение
69×[x] Expected at least 1 space before "."; 0 found
69×[x] Expected at least 1 space after "."; 0 found
4×[x] The first expression of a multi-line control structure
2×[ ] Line exceeds 120 characters; contains 126 characters
1×[ ] Line exceeds 120 characters; contains 161 characters
1×[ ] Line exceeds 120 characters; contains 124 characters

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

разтиппример
19×ArgumentTypeCoercionsrc/Billing/BillingService.php:107
10×MissingOverrideAttributesrc/Command/BillingRateCommand.php:20
8×MixedAssignmentsrc/Billing/BillingService.php:239
5×MixedMethodCallsrc/Billing/BillingService.php:242
5×MissingClassConstTypesrc/Domain/Model/PriceLine.php:15
4×MixedArgumentsrc/Billing/BillingService.php:117
4×UndefinedInterfaceMethodsrc/Command/BillingRateCommand.php:34
3×MixedOperandsrc/Command/BillingReportCommand.php:96

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

разсообщениепример
6×Parameter #1 $num1 of function bcadd expects numeric-string, string given.src/Billing/Money.php:46
6×Parameter #2 $num2 of function bcmul expects numeric-string, string given.src/Billing/BillingService.php:195
4×Call to an undefined method Symfony\Component\Console\Output\OutputInterface::getErrorOutput().src/Command/BillingRepriceCommand.php:45
3×Parameter #2 $num2 of function bcadd expects numeric-string, string given.src/Billing/BillingService.php:242
1×Parameter #1 $array (list<array{user: string, model: string, in: int, out: int, cr: int, cw: int, cost: string}>) of arrsrc/Billing/BillingService.php:126
1×Possibly invalid array key type int|null.src/Billing/BillingService.php:224

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

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

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

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

0246810060120180240300360App\Import\PriceCatalogParserLOC 204 · LCOM4 0 · WMC 54 · cc_max 20PriceCatalogParserApp\Billing\BillingServiceLOC 334 · LCOM4 3 · WMC 35 · cc_max 10BillingServiceApp\Domain\Usage\UsageEventLOC 192 · LCOM4 10 · WMC 23 · cc_max 1App\Import\UsageEventParserLOC 92 · LCOM4 0 · WMC 18 · cc_max 8App\Command\BillingReportCommandLOC 97 · LCOM4 4 · WMC 17 · cc_max 7App\Domain\Provider\ProviderLOC 129 · LCOM4 6 · WMC 17 · cc_max 6App\Command\PricesImportCommandLOC 139 · LCOM4 2 · WMC 16 · cc_max 5App\Domain\Model\PriceLineLOC 126 · LCOM4 7 · WMC 16 · cc_max 2App\Billing\MoneyLOC 52 · LCOM4 0 · WMC 9 · cc_max 5App\Command\UsageImportCommandLOC 79 · LCOM4 2 · WMC 9 · cc_max 7App\Domain\Model\ModelLOC 61 · LCOM4 4 · WMC 7 · cc_max 1App\Command\BillingRepriceCommandLOC 42 · LCOM4 2 · WMC 5 · cc_max 3App\KernelLOC 23 · LCOM4 1 · WMC 5 · cc_max 3App\Command\BillingRateCommandLOC 35 · LCOM4 2 · WMC 4 · cc_max 2App\Import\ImportExceptionLOC 3 · LCOM4 0 · WMC 0 · cc_max 0LOC классакомпоненты LCOM4

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

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

BillingServiceBillingServiceMoneyMoneyBillingRateCommandBillingRateCommandBillingReportCommandBillingReportCommandBillingRepriceCommanBillingRepriceCommanPricesImportCommandPricesImportCommandUsageImportCommandUsageImportCommandModelModelPriceLinePriceLineProviderProviderUsageEventUsageEventImportExceptionImportExceptionPriceCatalogParserPriceCatalogParserUsageEventParserUsageEventParserKernelKernelBillingService → Money · 1BillingService → Model · 1BillingService → PriceLine · 1BillingService → Provider · 1BillingService → UsageEvent · 1BillingRateCommand → BillingService · 1BillingReportCommand → BillingService · 1BillingReportCommand → Money · 1BillingRepriceCommand → BillingService · 1BillingRepriceCommand → Money · 1BillingRepriceCommand → ImportException · 1BillingRepriceCommand → PriceCatalogParser · 1PricesImportCommand → Model · 1PricesImportCommand → PriceLine · 1PricesImportCommand → Provider · 1PricesImportCommand → ImportException · 1PricesImportCommand → PriceCatalogParser · 1UsageImportCommand → UsageEvent · 1UsageImportCommand → ImportException · 1UsageImportCommand → UsageEventParser · 1Model → PriceLine · 1Model → Provider · 1PriceLine → Model · 1PriceLine → Provider · 1Provider → Model · 1Provider → PriceLine · 1UsageEvent → PriceLine · 1PriceCatalogParser → Money · 1PriceCatalogParser → PriceLine · 1PriceCatalogParser → ImportException · 1UsageEventParser → ImportException · 1BillingService → DateTimeImmutable ×1, EntityManagerInterface ×…Money → bcadd ×1, ltrim ×1, str_repeat ×1, …BillingRateCommand → Command ×1, InputInterface ×1, OutputInter…BillingReportCommand → DateTimeImmutable ×1, DateTimeInterface …BillingRepriceCommand → Command ×1, InputArgument ×1, InputInte…PricesImportCommand → EntityManagerInterface ×1, Command ×1, In…UsageImportCommand → EntityManagerInterface ×1, FILE_IGNORE_NEW…Model → ArrayCollection ×1, Collection ×1, Column ×1, …PriceLine → DateTimeImmutable ×1, DateTimeZone ×1, Column ×1, …Provider → ArrayCollection ×1, Collection ×1, Column ×1, …UsageEvent → DateTimeImmutable ×1, DateTimeZone ×1, Column ×1, …ImportException → RuntimeException ×1PriceCatalogParser → DateTimeImmutable ×1, DateTimeInterface ×1…UsageEventParser → DateTimeImmutable ×1, DateTimeInterface ×1, …Kernel → LoaderInterface ×1, Kernel ×1, false ×1внеш.CommandDomainImport

Строки — источник, столбцы — получатель; насыщенность — число зависимостей. Классы отсортированы по неймспейсам: слои подписаны на диагонали, жирные линии их разделяют; жёлтый столбец «внеш.» — исходящие связи к типам вне решения (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)
BillingRateCommand101.00
BillingReportCommand201.00
BillingRepriceCommand401.00
PricesImportCommand501.00
UsageImportCommand301.00
BillingService530.62
PriceCatalogParser320.60
UsageEventParser110.50
Model240.33
Provider240.33
UsageEvent120.33
PriceLine260.25
Money040.00
ImportException050.00

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

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

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

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

КлассLOCметодовWMCcc maxLCOM4
PriceCatalogParser204754⚠ 20 0
BillingService3341135 10⚠ 3
UsageEvent1922323 1⚠ 10
UsageEventParser92418 8 0
Provider1291217 6⚠ 6
BillingReportCommand97517 7⚠ 4
PricesImportCommand139616 5⚠ 2
PriceLine1261516 2⚠ 7
UsageImportCommand7939 7⚠ 2
Money5249 5 0

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

МетодCCLOC
PriceCatalogParser::parseData2062
PriceCatalogParser::parsePrices1960
BillingService::reprice1072
UsageImportCommand::execute762
BillingService::report665

Полные данные — 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.

3.5 из 4ось A «код и архитектура»
4 из 4ось B «процесс и коммуникация»
20 счистое время ревью
34.9Kтокенов (вход 34.3K, $0.0054)

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

Чекер: обязательных 11/11, бонус 2/2
Метрики: phpcs 152, psalm 74, phpstan 31, loc 1260
AST: cc_max 20, lcom4>1: 10, превышения порогов {'method_cc_over_10': 2, 'method_loc_over_50': 5, 'class_loc_over_300': 1, 'class_wmc_over_50': 1, 'class_lcom4_over_1': 10}

Обоснование

ревьюер яруса 3
Чекер: обязательные 11/11 и бонус 2/2 — решение полностью рабочее. Домен смоделирован корректно (Provider/Model/PriceLine/UsageEvent), тарификация отделена от импорта и отчёта, деньги — bcmath (scale 16, единственное округление HALF_UP в Money::roundHalfUp), float для денег отсутствует. Edge-case'ы покрыты и тестами (границы оф-пик-окна вкл./искл., переход через полночь, нулевые токены, HALF_UP 14.2624985→14.262499), и ручными проверками (дубль request_id у разных провайдеров, partial --from/--to). Дефекты, снижающие оценку: N+1-запросы — в ratePending и UsageImportCommand на каждое событие/строку идут findOneBy/findProviderByCode, report тянет все события в память вместо агрегата; в computeCost отсутствующая ставка тихо превращается в '0' ($rates['input'] ?? '0'), что маскирует битый справочник; единый BillingService (report+rate+reprice, lcom4>1 у 10 классов, wmc>50) — границы слоёв условные; статанализ шумит (psalm 74, phpstan 31, phpcs 152). Артефакты образцовые: PR_DESCRIPTION фиксирует допущения (user_code, CREATE SCHEMA IF NOT EXISTS) и шаги приёмки, COMMIT_MESSAGE осмысленный, тесты (27 OK) реально добавляют уверенность — ось B на максимум.

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

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

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

3.5 из 4ось A «код и архитектура», вслепую
+0расхождение осей A: ярус 3 − ярус 4
17 счистое время судьи
34.5Kтокенов (вход 34.0K, $0.0054)
заметка судьи
Сильное решение: чёткая доменная модель (Provider/Model/PriceLine/UsageEvent), bcmath повсюду без float, одно округление события HALF_UP, окно через полночь покрыто тестами на границы, дедупликация по (provider, provider_request_id) с уникальным индексом, бонус reprice с дельтой. Слабости: тарификация сосредоточена в BillingService вместе с отчётом и репозиторными запросами (домен частично анемичный, вычисление стоимости в сервисе с EM, а не в чистом домене); N+1-запросы при поиске цены и дедупликации; молчаливые fallback-и `rates['input'] ?? '0'` могут скрыть ошибку данных; roundHalfUp написан вручную вместо простого приёма с bcmath-масштабом. Тесты добавляют уверенность, но только на чистых классах — приёмка команд описана, а не автоматизирована.

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

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