Скил structured-outputs-integration-gate — тест, который schema unit пропускает
При миграции обработки ответов LLM на API-side structured output validation одних schema unit тестов недостаточно. Скил объясняет граничный контракт, который ломается тихо, и единственный wrapper-path тест, который это закрывает.
Добавить schema-bound парсинг ответов в LLM-пайплайн изолированно просто. Новый валидатор компилируется, schema unit тесты проходят, реализация выглядит чисто. Сбой появляется позже — на границе между новым валидатором и уже существующим кодом постобработки, который тихо чистил ответы ещё до появления валидатора.
Скил structured-outputs-integration-gate нужен для того, чтобы поймать именно этот класс регрессий до code review. Триггер узкий: задача мигрирует от prompt-engineered извлечения JSON к structured-output или typed-parse эндпоинту API, либо добавляет слой post-parse валидации рядом с существующими хелперами для очистки ответов.
Почему schema unit тестов недостаточно
Schema unit тест проверяет валидатор изолированно. Он мокирует вызов провайдера, отправляет валидный payload, payload с неверной формой и пограничные значения полей. Это корректно фиксирует schema-контракт. Однако он не видит, что происходит, когда legacy-очиститель ответов и новый валидатор выполняются последовательно на одном payload.
Проблема — в порядке выполнения. Новый валидатор обычно запускается на распарсенном объекте модели раньше, чем у старых фильтров появляется шанс сработать. Если legacy-контракт гласил «тихо убрать эти токены из ответа», новый валидатор меняет это на «жёстко завалить весь ответ в момент появления одного из этих токенов». Schema тесты были зелёными, потому что никогда не подавали именно такой payload. Продакшн-поведение было сломано, потому что legacy-payload ответов содержат именно эти токены.
Wrapper-path тест
Гейт требует второго теста вместе со schema unit тестами. Wrapper-path тест проверяет полный обработчик от точки входа через все существующие фильтры постобработки и новый валидатор. Критический шаг — инъекция payload, который legacy-фильтры были предназначены убирать, с последующей проверкой сохранения исторического поведения: тихое удаление и успех, а не schema-unit поведение — жёсткое отклонение.
Этот тест является гейтом. Скил прямо указывает: ни один вид теста в отдельности недостаточен. Schema unit тест без wrapper-path теста оставляет граничный контракт ненаблюдаемым. Wrapper-path тест без инъекции legacy-strip payload полностью пропускает сбой.
Что делать при срабатывании гейта
Если план для затронутого обработчика не перечисляет оба вида теста, скил требует вернуться к генерации плана и добавить их до начала реализации. Если реализация уже идёт, скил требует удержать merge до тех пор, пока wrapper-path тест не станет зелёным и намеренно не попробует legacy-strip сценарий.
Антипаттерны, которые гейт отвергает, конкретны: schema unit тесты без wrapper-path теста даже при исчерпывающем покрытии; wrapper-path тест без инъекции legacy-filter payload; валидатор, срабатывающий до выполнения legacy-фильтра. Для третьего случая есть структурное решение: разбить валидатор на schema-only проверку и post-filter safety-net хелпер, чтобы legacy-фильтр выполнялся первым, а проверка «нет остатков» — после.
Откуда это правило
Скил документирует собственное происхождение. Тихая контрактная регрессия именно такой формы попала в feature-ветку, прошла schema-unit review и была поймана только при человеческой проверке merge request мейнтейнером, который держал legacy-filter контракт в памяти. Schema тесты были зелёными; продакшн-поведение было сломано. Один wrapper-path тест закрыл бы этот пробел до выхода на человеческий review.
Читайте что такое Datarim для понимания того, как скилы встраивают уроки в рабочий процесс, или изучите скил self-verification, чтобы увидеть, как подобные пробелы обнаруживаются на слое верификации.