Ревью и тесты: как движок сам себя проверяет
Что это
Движок не доверяет собственному «вроде ок». Он проверяет себя двумя независимыми механизмами: призмы (многоугловое ревью артефакта) и тестирование (QA с доказуемыми тестами). Оба экспортируются буквальным рецептом в пакет, чтобы работали даже на слабой модели.
Призмы — независимые критики
Призма — это угол независимой оценки от лица роли, а НЕ агент-исполнитель. Контракт: независимый вердикт pass | warn | block + находки с severity. Рецепт в пакете прямо требует:
Прогони артефакт через углы НЕЗАВИСИМО: каждый угол судит сам и не видит чужих вердиктов.
В каталоге 13 призм. Пять из них — вето-призмы (инварианты, поднятые в ранг судей): security (вето, вес 2), tenancy (вето, вес 2), gating (вето, вес 2), regression (вето, вес 2), data-integrity (вето, вес 2). У каждой призмы есть blockIf — красные линии, и trigger — по каким фазам/артефактам/стеку она включается (a11y — только когда есть UI, data-integrity — только на миграциях).
Синтез вердиктов (детерминированное правило из orchestration.synthesize):
Любой critical у вето-призмы → BLOCK; иначе BLOCK, если сумма весов block-голосов ≥ порога; ничья → BLOCK (в пользу безопасности).
То есть одна security-призма с critical перевешивает любое число «всё хорошо» от остальных.
Оркестрация: adversarial-verify ≥2 голоса
Поверх призм — правило голосов (verifyVotes, в Quest = 2):
ПЕРЕД сдачей шага собери минимум 2 независимых голоса «готово»: первый — твоя реализация; остальные — скептики-ревьюеры, которые ИЩУТ, что сломается и где приукрашено. Один голос «вроде ок» — НЕ готово.
Плюс fan-out: «один пишет — ДРУГОЙ проверяет», и ЗАКОН РЕСУРСА: «лучше сжечь токены на второй угол, чем сдать непроверенное и переделывать».
Ревью до идеала (слой конструктора)
Статус.
engine.review— слой конструктора: поддержан схемой и экспортёром, но в базовый флагманский сид Quest пока не входит (в разработке для флагмана). Призмы иverifyVotesвыше — часть базового Quest; описанный здесь цикл полировки вы добавляете сами.
engine.review добавляет планку выше pass/fail: цикл «улучшай → пере-суди призмами», пока нет находки ≥ порога severity (minSeverity) ИЛИ достигнут кап полировки (maxPolish, дефолт 4). Рецепт разделяет DONE (годно к сдаче) и IDEAL (полировать нечего) — «не путай сдал и идеально».
Тестирование — доказуемые тесты (слой конструктора)
Статус.
engine.testing(RED-FIRST + мутационный гейт) — слой конструктора: поддержан схемой и экспортёром, но в базовый флагманский сид Quest пока не входит (в разработке для флагмана). Купленный Quest не приносит этот QA-рецепт из коробки; это то, что вы включаете, описывая свой движок в конфигураторе.
engine.testing включает QA-рецепт с четырьмя жёсткими правилами:
- Тест-план и кейсы выводятся ИЗ критериев готовности (каждый required → минимум один кейс).
- RED-FIRST: сначала падающий тест, потом код. «Тест, который никогда не падал, не проверил НИЧЕГО.»
- BANNED — тавтология: тест ОБЯЗАН уметь падать;
expect(true).toBe(true)запрещён. - Мутационный гейт: прогнать реальный инструмент (StrykerJS для JS/TS, mutmut для Python). Выживший мутант = дыра в тестах. Нет инструмента → FAIL-CLOSED-SKIP (честно: гейт пропущен, а не пройден).
- Покрытие — только поиск дыр, НЕ цель само по себе.
Артефакты: TEST-PLAN.md и BUG-LOG.md.
Worked-пример
Вход: движок дописал SQL-выборку заказов в multi-tenant backend и говорит «готово». В этом примере движок собран с включённым слоем engine.testing (шаг 5) поверх базового Quest (шаги 1–4, 6).
Что делает движок:
- Fan-out (Quest): реализация — одна роль; ревью — другая (скептик).
- Призмы независимо (Quest):
tenancyсмотрит запрос и находитSELECT * FROM orders WHERE id = $1безorganization_id→ вердикт block, critical («cross-tenant чтение достижимо»).securityпроверяет параметризацию — ok.perfотмечает отсутствие индекса — minor. - Синтез (Quest): critical у вето-призмы
tenancy→ общий BLOCK, несмотря на то что security и perf почти зелёные. - loop-until-dry (Quest): движок чинит КЛАСС — грепает весь модуль на запросы без тенант-фильтра (не только этот), добавляет
AND organization_id = $2везде, пере-судит толькоtenancy→ теперь pass. - Тесты (слой конструктора
engine.testing): выводит кейс «запрос юзера B не видит заказы юзера A», пишет его RED-FIRST (сначала падает на старом коде), затем зелёный на новом. Мутационный гейт StrykerJS убивает мутанта, который удалял тенант-условие. Этот шаг доступен, только если вы добавили слой testing. - Второй голос (Quest): скептик-ревьюер подтверждает — 2 независимых «готово».
Результат: артефакт сдан только после того, как вето-призма стала зелёной; при включённом слое testing регрессия ещё и закрыта тестом, способным падать, а мутант убит. Одна такая проверка ловит целый класс cross-tenant утечек, а не один инстанс.
Честная оговорка
Сначала о статусе: призмы, синтез вердиктов и правило голосов verifyVotes — часть базового Quest. Слой полировки engine.review и QA-рецепт engine.testing (RED-FIRST + мутационный гейт) — слои конструктора, во флагманский сид пока не входят (в разработке). Мутационный гейт из worked-примера работает, только если вы этот слой добавили.
Дальше о границе гарантии. Мутационный гейт — ENFORCED только там, где реальный инструмент запущен (есть код выхода); иначе честный FAIL-CLOSED-SKIP. Синтез призм и правило голосов — детерминированное правило, но их исполнение на Cursor/AGENTS без рантайма остаётся ADVISORY (модель следует рецепту, код выхода не блокирует). На Claude Code и в Cursor часть этого можно закрепить hook'ом; git-pre-commit/CI дают ENFORCED-пол на любом таргете. Конфигуратор помечает эту границу честно.