Накопитель зависит от порядка обхода
Проверка на повторы устроена так: инструмент идёт по страницам, складывает увиденное в накопитель и сравнивает очередную страницу с тем, что уже накоплено. Логика безупречная — и порождает три разных вранья.
| Поведение | Как выглядит | Почему это ложь |
|---|---|---|
| Первая страница пары | всегда зелёная | на момент её проверки накопитель ещё не содержит второй — сравнивать не с чем |
| Вторая страница пары | красная | «виноватой» назначается вторая по порядку обхода, а не та, что хуже по существу |
| Пересборка подмножества | зелёный по всему подмножеству | накопитель пуст, сравнивать снова не с чем: дефект есть, отчёт чистый |
Третья строка опаснее двух первых, потому что совпадает с обычным рабочим сценарием. Правишь десять страниц, гоняешь проверку по этим десяти, получаешь зелёный — и относишь его ко всему сайту. Проверка при этом честно сказала «среди этих десяти повторов нет».
Правило, которое мы из этого сделали. Пачечная приёмка гоняется только по полному корпусу и никогда по дельте. Прогон по изменённым страницам допустим как быстрый черновой контроль, но его результат не пишется в приёмку — там принимается только полный проход.
Уникальность — свойство корпуса, а не страницы
Постраничная проверка отвечает на вопрос «хороша ли эта страница сама по себе»: заголовки на месте, разметка валидна, текст читается. Вопрос «отличается ли она от трёхсот соседних» постраничная проверка не задаёт — и не может задать, потому что соседних не видит.
Ровно та же граница у агента, который пишет страницу. В его контексте лежит одна страница и задание к ней. Шаблонный оборот, размноженный по всему корпусу, для него не отличается от удачной формулировки: обе выглядят нормально в пределах одного экрана.
Экономия на модели — только там, где проверка доказана
Каскад «сначала дешёвая модель, дорогая только при сомнении» экономит заметно, и соблазн раскатать его на все проверки большой. Мы разрешили его в 5 гейтах из примерно 17 — ровно в тех, где заранее доказали, что проверка ловит дефект.
Доказательством считается мутационный тест: в проверяемое вносится поломка, и гейт обязан покраснеть. У одного из разрешённых гейтов все 6 правил ловят свою мутацию, у другого 5 инвариантов отработали в обе стороны, у третьего проверено 8 сцен в режиме «сломать — починить».
Формулировка, ради которой всё это делалось. Слабый гейт плюс каскад равно систематическая деградация, выглядящая зелёной. Слабый гейт сам по себе просто бесполезен; в паре с дешёвой моделью он начинает штамповать подтверждения качества, которого нет, — и чем больше объём, тем больше таких подтверждений.
На двух самых жёстких гейтах каскад запрещён совсем. Логика простая: там, где красный результат останавливает работу, цена пропущенного дефекта выше любой экономии на модели.
Общее во всех трёх случаях
Зелёный говорит: «эта проверка на этом наборе входных данных не нашла того, что умеет находить». Всё остальное — достройка читателя. Накопитель не нашёл повтор, потому что сравнивать было не с чем. Постраничный чекер не нашёл шаблонность, потому что не видел корпуса. Дешёвая модель не нашла дефект, потому что её никто не проверял на способность его находить.
Практический смысл: рядом с каждым зелёным нужно держать ответ на вопрос «что именно эта проверка умеет ловить и проверяли ли мы это». Без ответа зелёный не читается вообще.
Что сделать у себя
- Найдите у себя проверки с состоянием. Всё, что копит увиденное между элементами: дедупликация, поиск повторов, сверка сквозной терминологии. Любая такая проверка зависит от порядка обхода.
- Прогоните тест на порядок. Один и тот же набор в прямом и обратном порядке. Если «виноватые» поменялись местами — накопитель у вас есть и он врёт именно так.
- Запретите приёмку по дельте. Для накопительных проверок результат прогона по подмножеству не пишется в приёмку ни при каких условиях.
- Разделите проверки на постраничные и корпусные. Второй список у большинства команд пустой, и это не потому, что там всё хорошо.
- Не подпускайте дешёвую модель к недоказанной проверке. Сначала мутационный тест: вносим поломку — гейт краснеет. Нет теста — нет права экономить на модели.
- На останавливающих работу гейтах каскад не включайте. Там экономия считается от цены пропуска, а не от цены запуска.