Вторую сторону сверки можно заморозить, но проверка при этом меняет род, и это надо назвать вслух
Когда из репозитория уезжает то, с чем сверялись, выбор не «выбросить проверку или оставить как есть», а третий: записать ответы уезжающей стороны в таблицу и сравнивать с ними. Проверка выживает, но перестаёт быть тем, чем была.
Было — дифференциальная: две независимые реализации согласны, и расхождение любой из них видно. Стало — золотая: программа даёт те же байты, что давала в день заморозки. Регресс ловится по-прежнему, побайтово. Расхождение двух реализаций не ловится вовсе, потому что второй реализации нет.
Разница существенная, и прятать её нельзя: читатель отчёта, увидев «сверка зелёная», иначе решит, что согласие двух реализаций всё ещё проверяется.
Когда заморозка оправдана. Считать надо не строки проверки, а то, что она единственная делает. У нас: четыре проверки были ЕДИНСТВЕННЫМ местом, где flang/core/parser.flang (262 КБ, самая большая программа дерева) не просто разбирается и печатается, а ВЫЧИСЛЯЕТСЯ. Выбросить их значило бы оставить 450 КБ кода на языке без единого исполнения.
Три ловушки, и все три поймал прогон, а не рассуждение.
- Ключом таблицы стоял
JSON.stringifyвхода. Он превращает-0в"0", аNaNи бесконечности — вnull. Значит входы{сумма: -0}и{сумма: 0}получили ОДИН ключ, вторая запись затёрла первую, и проверка «ноль не равен минус нулю» покраснела на подменённом ответе. Подпись входа пришлось писать свою, где эти значения названы поимённо.
node --testизолирует файлы процессами. Три файла в одном запуске писали таблицу каждый на своём выходе, и последний затирал остальных: в таблице оказывались ответы одного файла из трёх, а прогон при этом был зелёный — в режиме записи все ответы берутся у настоящего эталона. Запись идёт по одному файлу за прогон.
- Промах по таблице обязан ронять, а не пропускать. Первая редакция отдавала промах наружу исключением, а загрузчик корпуса ловил его старым
catch { }— «это не документ ядра, пропускаем». Убранная из таблицы запись давала «прошло 92, упало 0»: модель просто исчезала из сверки, красных не прибавлялось, прибавлялось молчание. Тот же класс, что checks-that-stopped-comparing.
Чем подтверждено. Ветка work/bez-fts, коммит «Вторая сторона четырёх сверок ядра заморожена», 16 августа 2026. Таблица снята прогоном самих проверок против настоящего dist/src/index.js: 146 записей compile (47 из них — отказы намеренно сломанных моделей) и 36 682 записи evaluateUtility, 2,2 МБ. Прогон четырёх проверок БЕЗ собранного эталона: 321 из 321. Снятие правки проверено обоими способами: запись убрана — падение с текстом «замороженного ответа нет»; запись подменена одним байтом — падение сверки байтов с указанием места; таблица возвращена — ноль красных.
Чем ограничено. Годится там, где уезжающая сторона детерминирована и её выход сериализуем. Не годится, если проверка обязана ловить именно расхождение двух живых реализаций, — тогда заморозка даёт ложное спокойствие, и честнее проверку удалить.
Связано: byte-for-byte-comparison, checks-that-stopped-comparing, a-removal-must-turn-a-test-red, minus-zero-is-a-class, twin-lags-behind-the-reference
Перед заморозкой надо спросить, КЕМ свидетель служил проверке: вторым мнением или реализацией
19 августа 2026, ветка work/adr-pakety, стирание пяти свидетелей яруса оракулов (src/{setoid,functor,sets,grid,svoystva}.mjs, 1 801 строка по git diff --stat). Заморозка — не первый ход, а третий. Первые два дешевле, и проверять их надо прогоном, потому что по имени файла они не различаются.
Роль первая: свидетель как ВТОРОЕ МНЕНИЕ. Проверка называется «совпадает со свидетелем побайтово» и сравнивает два ответа. Такую заменить нечем: подставить на место стёртого тот же слой — значит сравнивать слой сам с собой и зеленеть вечно. Она снимается, и число снятых называется. Снято 22 из 5 041.
Роль вторая: свидетель как РЕАЛИЗАЦИЯ. Проверка называется «нерефлексивное отношение отвергается» и зовёт свидетеля просто потому, что он был под рукой, когда её писали. Предмет её — поведение ЯЗЫКА, а не согласие двоих. Такая переживает стирание целиком: импорт переставляется на слой на flang, вызовы становятся await, и не теряется ни одна. Так уцелели 26 проверок cat-equality, 26 cat-sets, 15 cat-transform, 15 cat-link — 82 из 82.
Роль третья, найденная прогоном: свидетель как ТОЧКА ОТСЧЁТА ДЛЯ ПОРЧИ. Десять проверок self-grid.test.mjs ломают эталон в одно слово и требуют, чтобы сверка покраснела. Им нужен не живой свидетель, а любое число, снятое ДО порчи. Заморозка тут работает лучше всего: запись отпечатков ответов на 296 программах (49 КБ), и все десять краснеют как краснели. Потеряно ноль.
Разложение по ролям и есть весь результат. 1 801 строка стёрта, 22 проверки снято. Считай я по файлам, а не по ролям, — снял бы под сотню: в self-setoid.test.mjs 33 проверки, и предметом свидетеля были 11.
Проверка того, что запись не подделка. У яруса оракулов запись была снята раньше, свидетелем (fixtures/zapis-oracula.json, отпечаток 54f7b210…). После подстановки слоя на flang на место трёх стёртых свидетелей отпечаток совпал, не будучи пересчитан, и посчитался за 3,2 с на 285 программах. Совпадение замороженного отпечатка — единственное, что отличает замену свидетеля слоем от тихой пересъёмки записи под новый ответ.
Чем ограничено. Роль «второе мнение» и роль «реализация» различаются НЕ по файлу, а по отдельной проверке внутри файла: в self-setsmonad.test.mjs живут обе. Разбирать приходится поимённо, и автоматики для этого нет.