Skip to content

feat/corpus-audit - #18

Merged
4dand merged 38 commits into
mainfrom
feat/corpus-audit
Sep 19, 2026
Merged

4dand merged 38 commits into
mainfrom
feat/corpus-audit

Conversation

@4dand

@4dand 4dand commented Sep 11, 2026 •

Copy link
Copy Markdown
Member

Что меняет PR

Ревизия корпуса результатов целиком: 1820 генераций от 52 моделей проверены инвариантами
поверх готовых оценок, дефекты харнесса исправлены, правила оценки уточнены, обе категории
пересчитаны. Релиз v1.10.0.

Первопричина общая: гейты проверяли определение бенчмарка эталонами, а линейка ломалась
на невежливом коде моделей. Зелёный prism check о здоровье корпуса не говорил ничего,
потому что результаты не проверял никто.

В отличие от первой редакции этого PR, здесь менялись и правила: протокол L1 поднят до
1.4.0, конституция до 1.3.0, рубрика эксперта до 1.1.0.

Отчёт с разбором по осям, таблицами дрейфа и списком нерешённых вопросов —
docs/audit-2026-09.md.

Главный результат

Утверждением без оснований считается балл, сообщающий о том, чего измерение не проверяло.
Истина берётся из сегодняшних данных и прикладывается к обоим состояниям оценок.

Утверждение main ветка
P = 0, хотя прогон до базы не дошёл 216 0
S выше 6 у модуля, который не компилируется 215 0
P = 10 при полностью провалившихся тестах 7 9
Итого 438 9

Выросшая строка — названный предел метода, а не скрытый дефект: у всех девяти записей
технологический журнал подтверждает, что обращение к базе состоялось.

Изменения правил — протокол L1 1.4.0

Все правки об одном: ноль означает проваленную проверку, а не несостоявшуюся.

  • Ось P отдаёт «не измерено» вместо нуля, если прогон не дошёл до обращений к метаданным.
    Оси SMOP независимы по смыслу, но у P и M общий канал измерения — прогон 1С; молчание
    канала есть отсутствие свидетельства, а не свидетельство против. В категории B ось измерена
    у 745 записей из 1040: у 219 не собрался модуль, у 47 не разобрался текст запроса, у 29 код
    ни разу не обратился к данным.
  • Q публикуется только вместе с охватом осей (quality_score.coverage_required). Q
    усредняет измеренные оси, поэтому выпадение оси его поднимает, и две записи с разным
    знаменателем несравнимы.
  • Ось S: потолок 6 для модуля, который не собирается (compile_check.ceiling). Побочно
    переписана рубрика эксперта: прежняя читала ступени как «сколько правок до компиляции» и
    отдавала 8 несобравшемуся модулю, из-за чего L1 и L2 расходились бы на ступень в самом
    частом случае. Разметки ещё нет, поэтому правка бесплатна.
  • Граница осей P и M — по свидетельству, а не по маркеру. Кривой текст запроса больше не
    провал P: он уже наказан падением M. Чистым такой тест при этом не становится и выбрасывается
    из знаменателя, иначе 6 записей прыгнули бы с 0 сразу на 10.
  • Технологический журнал в корректностном прогоне (P.signal.db_access): один факт —
    обращался ли код кандидата к данным. Записей с P=10 при полностью упавших тестах было 36,
    осталось 9.
  • Таймауты переведены на процессорное время (cpu_limit_s): балл перестал зависеть от
    загрузки машины. Сторож по стенным часам остался, но означает «не измерено», а не «медленно».

Исправлено в харнессе

  • Инструментатор оси O кат. A ломал корректный код: приёмник брался одним токеном, арность
    аналогов не совпадала с 1С, а переписанный смысл Найти молча ломал Колонки.Найти. Семь
    решений A1 получили балл O.
  • Классификатор платформенных ошибок знал один метод исполнения запроса — мимо шли
    виртуальные таблицы регистров, пакетный запрос и запись в поле только для чтения. Список
    маркеров переехал в metrics/error_taxonomy.yaml.
  • Кат. B: компилятор запускается и без точки входа — раньше S=10 выставлялся из пустого
    списка ошибок без запуска компилятора.
  • Сегфолт компилятора 1С на модуле кандидата стал виной кандидата, а не «инфраструктурой».
  • Английские ключевые слова BSL учитываются детекторами точки входа, парностью и автоэкспортом.
  • Инфраструктурный сбой перестал выглядеть дефектом модели: поле вины blame и корзины
    INFRA.*, этап «окружение» в воронке.
  • Нагрузочные замеры кат. B не кэшировались вовсе — каждый пересчёт правил заново гонял 1С
    по всем задачам с perf.yaml.

Стенд задач

Ревизия выбросов дважды упёрлась в дефект стенда, а не в сложность условия.

  • B1 и B14: у движений регистра не была задана дата, генератор ставил текущую, и решение,
    честно передавшее её в виртуальную таблицу остатков, видело пустой регистр. Эталон проходил
    лишь потому, что параметр не передаёт вовсе.
  • B8 расширен с двух тестов до пяти: расходных движений в данных не было, колонку «Расход»
    можно было захардкодить нулём.
  • B3 и B5: тесты сами резали текст исключения и теряли платформенный маркер.
  • B19: условие противоречило данным задачи.

Данные и воспроизводимость

  • Перегенерированы 13 ответов, обрезанных прежним потолком вывода; четыре из них решают свою
    задачу полностью.
  • Пересчитаны обе категории. Баллы изменились у 410 записей из 1820 (79 в A, 331 в B).
    Средний общий балл почти не сдвинулся: 7.31 → 7.33. По категориям разнонаправленно:
    A 7.61 → 7.51 (потолок S растянул шкалу вниз), B 7.02 → 7.15 (ушли нули, которых ось не
    имела права ставить). Средний балл по оси P вырос с 6.12 до 8.14 не потому, что модели стали
    лучше, а потому что из среднего ушли фальшивые нули.
  • prism artifacts разворачивает запись оценок обратно в логи 1С, как они были: ключ
    прогона пишется в оценку (detail.M.run_key). Раньше ответ на «почему тут такой балл» стоил
    нового прогона 1С.
  • results/artifacts.jsonl — сырьё всех опубликованных оценок (1027 прогонов, 0.7 МБ) едет
    в репозиторий. Кэш замеров остаётся вне git, но клонировавший теперь может проверить балл,
    а не только увидеть его.
  • Время генерации восстановлено по истории репозитория (tools/backfill_generated_at.py).
    Прежняя дозаполнялка брала даты чекпойнтов, а те переписывались при каждом прогоне: у 1807
    записей стояла дата последнего пересчёта, и модель, объявленная в июне, датировалась
    сентябрём. Теперь время берётся у коммита, первым принёсшего ответ, ключ — тройка «модель,
    задача, хеш ответа». Сверка с журналом: расхождений больше трёх дней нет.

Документация и витрина

  • Документация приведена к текущему состоянию: оракул оси S, формула Q с охватом, правило
    техжурнала, prism artifacts, кэш замеров, словарь ошибок, карта проекта схемой.
  • Новая страница Глоссарий. Страница «Что умеет сейчас» слита с «Честными границами»
    (половина её разделов дублировала архитектуру и лидерборд), старый адрес остался редиректом.
  • Витрина: охват осей публикуется везде, где публикуется Q, включая выгрузку в SVG и PNG.
    Неизмеренная ось больше не рисуется нулевым столбцом ни на сайте, ни в prism charts.

Не закрыто

  • Девять записей с P=10 при провалившихся тестах — предел разбора по логу, показан аудитом
    отдельным пунктом.
  • Четыре класса платформенных ошибок по логу не опознаются, нужен классификатор по исходнику.
  • Задачи-выбросы: B19 решают 5 моделей из 52, ревизия условия не проводилась.
  • Экспертной разметки L2 нет ни одной записи, каппа L1↔L2 не посчитана.

Чеклист

  • prism check — зелёный, с эталонами обеих категорий
  • make test-fast — 463 теста зелёные (всего в наборе 491); полный make test с
    интеграцией не гонялся
  • make lint — зелёный
  • prism audit — 5 нарушений и 9 предупреждений, все описаны в отчёте как открытые вопросы
  • metrics/smop.yaml правился осознанно: добавлены coverage_required и блок not_measured
    у оси P, версия поднята до 1.3.0
  • пороги и веса — в metrics/*.yaml; порог отчёта аудита M_ZERO_TASK_THRESHOLD остаётся
    в harness/audit.py (это порог отчёта, а не оценки)

@4dand
4dand marked this pull request as ready for review September 19, 2026 16:34
В запись прогона добавлены max_tokens, reasoning_effort, generated_at и provenance. Дефолт потолка вывода менялся четыре раза, а в данных этого не было видно, и пустые ответы reasoning-моделей под потолком 4096 нельзя было отличить от молчания модели.

tools/backfill_provenance.py дозаполняет старые записи по датам чекпойнтов с пометкой «восстановлено» и отказывается писать, если хоть один прогон выдал больше предполагаемого потолка.
Все 1785 записей получили потолок вывода и дату по чекпойнтам: 4096 у 315, 16384 у 169, 65536 у 1301.

Перегенерированы семь ответов, обрезанных прежним потолком 4096: GLM-4.7 Flash (B1, B6, B8, B14, B15), DeepSeek V4-Flash (B8), Gemini 2.5 Flash Lite (B20). Ещё шесть ждут доступности каналов Яндекса и Сбера.
id модели привязан к каналу доступа и у семи моделей раздвоился после смены канала, поэтому выборочный пересчёт по id промахивался и молча оставлял старые оценки. Ключ теперь имя модели, а фильтр, не совпавший ни с одной парой, даёт явный отказ.
prism check проверяет определение бенчмарка, а результаты не проверял никто. Линейку мы
гоняем вежливым кодом эталонов, а мерим ею невежливый код моделей — там она и ломается.

Аудит ничего не правит, только печатает, где данные противоречат сами себе: сироты,
дубли, условия прогона, ось O при исполнившемся коде, влияние неизмеренных осей на Q.
Подмена для замера оси O брала приёмник одним токеном, поэтому `ТЗ.Колонки.Найти(x)`
превращалось в вызов несуществующего имени. Ещё арность хелперов не совпадала с 1С.

Корректный код из-за этого не исполнялся и терял балл — семь решений A1.

Приёмник теперь берётся целиком, хелперы начисляют цену и делегируют смысл встроенной.
Первая версия правки переписывала смысл `Найти` и тихо ломала поиск по колонкам.
BSL принимает английские ключевые слова наравне с русскими, а детекторы точки входа, проверка парности и автоэкспорт знали только русские. Рабочий модуль с function ... Экспорт считался «функция не найдена», а незакрытый for each ... do — сбалансированным. Детектор категории A заодно видит процедуры, как в категории B.
…date fault

Раньше при отсутствии точки входа прогон возвращался до контейнера, и S=10 выставлялся из пустого списка ошибок без запуска компилятора. Теперь компилятор высказывается всегда, а тесты без точки входа не зовутся.

Коды завершения шагов пишутся в check.rc и enterprise.rc. Сегфолт компилятора на модуле кандидата (код 139, пустой лог) стал виной кандидата с S=0, а не «инфраструктурой, не измерено». Триггер одного случая: «Вызвать Исключение» двумя словами.
Категория A пересчитана целиком, категория B точечно. Дрейф к снимку до гигиены объяснён построчно: A 11 записей, B 14. Правила метрики и версия протокола не менялись.
Запись в технический журнал и короткая заметка в витринный: что пересчитано и почему.
У Яндекса кончилась квота. Модель та же, выключатель рассуждения канал слушает (проверено: 1910 токенов рассуждения без него, 0 с ним). Тариф взят из розницы канала, прямого прайса вендора нет.
Qwen3.6 на A3, A5, A6, A7, A8 и GigaChat 2 Max на B8: прежний потолок 4096 обрывал их ответы. Ни одной записи с обрывом по длине в корпусе не осталось.

Qwen3.6 на A6 решает задачу полностью, GigaChat на B8 получил честный S=10 вместо штрафа за нашу обрезку.
Модель пришла из main, оценённая прежней линейкой. Пересчёт обеих категорий не изменил ни одного балла: наши починки её не касаются.
Вердикт «собирается» давал BSL LS, а он парсер и снисходителен: 173 записи из 780 не
собираются в OneScript, и 66 из них имели S=8 или 10.

Теперь вердикт берёт движок, который этот же код исполняет. Число причин по-прежнему
считает BSL LS.
Смена правил оценки не меняет вход песочницы: код тот же, задача та же, инструмент тот
же. Значит и вывод движка будет тот же.

Кэш хранит сырьё, а не баллы, поэтому правка протокола применяется мгновенно ко всему
корпусу. Таймауты не кэшируются — это свойство загрузки машины.
Девять записей ушли с S=10 на 8: движок исполнения их модули не разбирает, а парсер этого не видел. Инвариант «S=10 при неразобранном модуле» теперь ноль.

Запись A14 GPT-5.6 Luna Max пришлось перемерить отдельно: под нагрузкой она не уложилась в лимит и потеряла балл O. Это F18, второй случай на той же записи.
Лимит считался по стенным часам, поэтому балл зависел от того, чем ещё занята машина.
Одна запись дважды теряла балл O под нагрузкой и дважды возвращала его на свободной
машине с теми же счётчиками.

Теперь бюджет процессорный. Сторож по часам остался, но впятеро больше и означает «не
измерено», а не «медленно».
Ось P ставила ноль всему, что до базы не дошло: модуль не собрался, функции нет,
запрос не разобрался. Ноль утверждает, что обращения к метаданным были и оказались
неверными, а их никто не проверял. Теперь там «не измерено» — 216 записей.

Ось S больше не даёт выше 6 модулю, который не компилируется: одна корневая причина
стоила 8 из 10 и тянула вверх общий балл. Рубрику L2 пришлось переписать под ту же
границу, иначе машина и эксперт расходились бы на ступень.

Рядом с Q теперь всегда охват осей. Q усредняет измеренное, поэтому выпадение оси его
поднимает, и две записи с разным знаменателем несравнимы.
Таблицы и site_data.json считаются из файлов оценок, а оценки изменились. Добавилась
колонка «полнота»: без охвата два одинаковых балла означают разное.

Верхушка не сдвинулась, сместилась середина категории B.
Кэшировался только удавшийся замер. Но замер срывается и тогда, когда падает сам код
кандидата, а это свойство кода и повторяется один в один. Из-за этого 358 записей
поднимали 1С заново при каждом пересчёте.

Не кэшируем теперь только пустой техжурнал — вот это про окружение.
Q усредняет только измеренные оси, поэтому одинаковое число при полноте 100% и 50%
означает разное. В консоли и markdown колонка уже была, на витрине нет.

Подпись показывается, только когда что-то не измерено: у полных записей это шум.
Тест, упавший на общем BSL, до базы обычно не добирается и про метаданные не говорит
ничего — а считался чистым. Отсюда 36 записей с P=10 при полностью провалившихся
тестах.

Корректностный прогон пишет теперь узкий техжурнал: одно событие, одно свойство.
Нужен один факт — было ли обращение к данным. Осталось 10 записей, и у всех обращение
подтверждено.

Заодно «Method not found» перестал врать: в 414 случаях из 522 метод в платформе есть,
просто у другого типа, и причина называет владельца.
У движений B14 и B1 не была задана дата, и генератор ставил текущую — момент самого
прогона. Виртуальная таблица остатков границу не включает, поэтому решение, честно
передавшее текущую дату, видело пустой регистр и теряло все тесты. Эталон проходил
только потому, что параметр не передаёт вовсе.

Даты проставлены, генератор больше не подставляет их молча. Остальные 14 задач дату и
так задавали.

B8 расширен с двух тестов до пяти: расходных движений в данных не было вовсе, так что
колонку «Расход» можно было захардкодить нулём, а необязательность склада не
проверялась ни разу.
Правило написано как `work/` и матчит только каталог, а у части разработчиков work —
это симлинк на быстрый диск. Один раз уже уехал в индекс.
Полный пересчёт 1040 записей. Нулей «до базы не дошли» было 216, стало 0. Записей с
P=10 при провалившихся тестах было 36, осталось 10, и все обоснованы.

Починка фикстур B14 вернула полный балл двум решениям, которые падали по вине стенда.

Расширение B8 градации почти не дало: частичный балл один. Значит бинарность там не от
грубого оракула, а оттого, что один кривой запрос валит все тесты сразу.
Правила менялись в два захода, но промежуточное состояние не покидало машину — ни один
балл по нему не опубликован. Наружу переход один. Плюс номер 1.5.0 уже занят релизом
репозитория.

В файлах оценок обновлена метка версии: правила те же, сменилось только имя.
Конституция получила правило охвата и блок «не измерено» у оси P,
рубрика эксперта переписана под границу шкалы между 8 и 6.
Менялось по существу, а номера версий оставались прежними.
Прежняя дозаполнялка брала даты файлов-чекпойнтов, а их переписывал
каждый повторный прогон. В итоге у 1807 записей из 1820 стояла дата
последнего пересчёта: модель, объявленная в июне, числилась сентябрьской.

Теперь время берётся у коммита, который первым принёс этот ответ.
Ключ тройной: модель, задача, хеш ответа. По одному хешу время уезжало
к чужой модели, потому что две модели нередко выдают одинаковый код.

Ответы, хеши и баллы не тронуты, пересчёт не нужен. Сверено с журналом
по всем 52 моделям: расхождений больше трёх дней нет.
Кэш замеров лежит вне git, поэтому клонировавший видел балл, но не мог
проверить, откуда он взялся: логи 1С оставались на машине прогона.

`prism artifacts --export` собирает в results/artifacts.jsonl.gz только
те прогоны, на которые ссылаются сами записи: 1027 штук и 75 КБ против
сорока трёх мегабайт всего кэша. Разбор записи сначала смотрит в кэш,
а если его нет, читает выгрузку.
В SVG и PNG ось без замера рисовалась нулевым столбцом и нулевой
вершиной радара, то есть читалась как провал по этой оси. Столбца
теперь нет, на его месте прочерк, у радара разрыв контура и пометка
в легенде.

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

Таблица способностей с колонкой подтверждений и роадмап переехали
туда, два роадмапа сведены в один список. Регион status:lb больше не
генерируется. Старый адрес остался редиректом, страница была в индексе.
Документация отстала от кода. Ось S: вердикт «собирается» даёт движок
исполнения, а не парсер. Формула Q: усредняются только измеренные оси,
поэтому рядом всегда охват. Ось P: добавлено правило техжурнала.

Описаны команда prism artifacts, кэш замеров как хранилище сырья,
словарь ошибок и переменные окружения, которых не было в справочнике.
В архитектуру добавлена карта проекта схемой.

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

Таблицы лидерборда и счётные бейджи пересобраны prism docs после
последнего пересчёта.
Вся ветка это одно событие для читателя витрины, а записей было две:
одна про исправления харнесса, другая про смену правил. Сведены в одну
от 19 сентября, с числами по категориям и ссылкой на отчёт.

Отчёт об аудите заведён в репозиторий: на него ссылается и журнал,
и описание PR.
Охват считается в штуках осей, а не в долях: 99 и 97 процентов на глаз
одинаковы, а разница между «почти всё измерено» и «половины нет» и есть
смысл величины.

Ось P с состоянием «не измерено» больше не получает ярлык потери баллов:
терять нечего там, где проверки не было. Причина называется словами,
из detail.P.unmeasured.

Ноль пройденных тестов разведён на три исхода: код вернул не то, упал
на обращении к базе, упал сам. Заголовок «Ответы неверны» подходил
только к первому и спорил с исходом записи наверху карточки.

В записи попали условия генерации: дата и время, потолок вывода,
температура, токены рассуждения и кэша, причина завершения, хеш ответа.

Журнал: абзац после списка больше не приклеивается к последнему пункту,
понимаются markdown-ссылки и версия в заголовке записи.
Конституция требует охват везде, где публикуется Q, а он стоял только
в широкой таблице. Добавлен в сводный лидерборд, в мобильные строки
обеих таблиц, в карточку модели и в выгружаемые картинки: их уносят
из контекста сайта, и знаменатель обязан ехать вместе с числом.

Ось без замера больше не рисуется нулём: ни столбцом в профиле, ни
вершиной радара. У моделей с неполным профилем появилась пометка
в легенде.

Вердикт на карточке получил числа и разбивку по категориям. Заодно
поправлены формулировки: «все тесты пройдены» говорится только при
настоящей десятке, иначе это спорило с подписью «решает 93 процента».

Тултип свой, а не браузерный: длинный текст переносится и не уезжает
за правый край окна.
Одно и то же состояние подписывалось двумя способами: в строке осей
прочерк, в разборе ниже N/A. Оставлен прочерк.

Под баллом категории появился охват, под полосой фактов — условия
генерации. В подписи к оси M сказано, почему там ноль, когда у соседних
осей «не измерено»: M и есть место, где фиксируется неработающий код.

График нагрузки: у шкалы появился запас сверху и деления, иначе плоский
ряд прилипал к верхней рамке, а подпись верхней точки уезжала за край.
Текст вокруг графика растянут по ширине блока.
MkDocs рисует mermaid сам, Astro о нём не знает и отдавал схему как
обычный блок кода. Теперь она рисуется на клиенте, импорт ленивый:
библиотека весит прилично, а схема есть не на каждой странице.

Колонка документации не давала себе сжиматься, поэтому широкая схема
распирала страницу и текст уезжал за экран. На узком экране схема
прокручивается вбок, а не мельчает до нечитаемости.

Подвал прижат к низу окна: на короткой странице он всплывал на середину
экрана и под ним оставалась пустая полоса.
Минор: менялись и правила метрики, и стенд задач. Протокол L1 1.4.0,
конституция 1.3.0, рубрика эксперта 1.1.0, таксономия ошибок 0.2.0.

Журнал дополнен разделом про документацию и витрину, номер версии
сведён по всем местам, где он публикуется.
Линеаризация ветки разрешала конфликт в пользу ветки и отбросила часть
изменений, пришедших из main: итоговые суммы токенов и стоимости в обоих
файлах результатов и доработку инструмента провенанса про пары, которые
появились после снимка.

Возвращено к состоянию, на котором гонялись проверки.
@4dand
4dand merged commit 7c4a3b4 into main Sep 19, 2026
2 checks passed
@4dand
4dand deleted the feat/corpus-audit branch September 19, 2026 16:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant