Прогон #2371 (ee678352) добавил в demo/guard/verify-guard.mjs пробу
гарда benchmark — и job «Смоки в браузере» была ПРОПУЩЕНА как
переиспользованная. Причина в HARNESS.smoke: ключ держал ровно
demo/smoke_*.mjs, а `demo/serve.mjs`, `demo/guard/**` и benchmark, который
проба запускает, в него не входили. Файл исполняется только в этой job,
его правка её ключ не меняла — проба уехала в dev, ни разу не
запустившись.
Пропущенная проверка выглядит точно как пройденная: это тот же дефект,
против которого заведён #430, только этажом выше — не тест не умеет
падать, а job не запускается. Заодно это значит, что правки #404 в
serve.mjs и любой из четырёх проб гарда всё это время могли быть
реюзнуты.
Критерий ключа теперь один и читаемый: всё, что job исполняет.
Benchmark'и берутся все, а не по имени, — любой может быть подключён к
пробам позже, и лишний прогон дешевле молчания. demo/fixtures/** не
добавлен: он в корпусе sourceFingerprint, который уже подмешан в ключ.
Тест проверяет обе половины: файлы попадают в оснастку И правка каждого
меняет ключ. На прежнем предикате он краснеет (проверено).
Гейты: npm test 1791 tests, 1790 pass, 0 fail. Этот коммит меняет ключ
смоков, поэтому job со смоками на нём прогонится по-настоящему — вместе
с той пробой, ради которой всё и затевалось.
Issue: #430
User-Visible: no
Аудит v1.71.0-beta.1 (§3.2 M1/M2/M3/M6/M9) прогнал по мутанту на каждый
контракт: пять снятий защиты не покраснили ни один тест. Восьмой подряд
случай проверок, не умеющих падать, и первый — в бете, закрывавшей #421,
задачу ровно об этом.
Каждый свидетель ниже проверен отрицательным прогоном: мутант краснит
ровно свой тест и не задевает остальные.
1. Полное декодирование растра. `test_supported_raster_headers_and_full_decode`
спрашивал w/h/mime — их даёт header-парсер; обрезка `PNG_1X1[:33]`
теряет IEND и отбивается там же. No-op блока Pillow оставлял 35 pass.
Новый свидетель: PNG с верными сигнатурой, IHDR, IEND, длинами и CRC,
но с текстом вместо zlib-потока в IDAT. Оба прежних теста
переименованы — их имена обещали то, чего они не проверяли.
Пропуск без Pillow остался (validate_asset глотает ImportError
осознанно), но в окружении с Home Assistant отсутствие Pillow теперь
красное само по себе — иначе свидетель молча скипался бы в каноне.
2. Канонизация SVG. `ValidatedAsset(canonical,…)` → `ValidatedAsset(data,…)`
не краснило ничего: все тесты смотрели w/h/mime, ни один — байты.
Свидетель сверяет байты целиком: пролог и комментарий не переживают
канонизацию, пустой элемент сжимается.
Вторая половина — `_check_size(canonical)`. Экранирование `>` в тексте
раздувает документ вчетверо: загрузка 1.84 МиБ канонизуется в 7.35 МиБ
и до сих пор проходила входной контроль. Свидетель — ровно такая.
3. Гард внешних URL. Все три «внешних» кейса корпуса ловились другими
правилами (тег не из словаря, атрибут не из словаря, ветвь href), и
`if False:` не краснило ничего. Свидетель: разрешённый тег, разрешённый
атрибут, пять токенов — сработать может только сам гард, сообщение это
подтверждает.
4. Проекция декора. Единственный кейс задавал flip_h и opacity: 2 → 1;
ожидание неотличимо от «opacity игнорируется», а flip_v не проверялся
вовсе. Свидетели: четыре комбинации отражений, opacity 0.4 и 0 против
заглушки, форма asset_id с согласованным url (прежняя строка ловилась
сравнением url, поэтому регулярку id можно было удалить незаметно).
5. Гард benchmark из #423. Доказательство было циклическим: тест вырезал
из текста подстроку и спрашивал регулярку, находит ли она её, — а
регулярка искала именно её. Динамический режим `--guard-probe`
существовал с #423 и не вызывался ни одним прогоном. Теперь он в
`demo/guard/verify-guard.mjs` (умеет аргументы и файл выше каталога),
а тест из обнаружения требует у нового page-benchmark режим пробы и
запись в верификаторе.
Мутантов в реестре стало восемь новых: четыре бэкендных, три юнитных,
один на пробах гарда. У #423 их было ноль — единственная задача с
циклическим тестом и она же единственная без мутантов.
Гейты: typecheck зелёный; npm test 1790 tests, 1789 pass, 0 fail;
pytest без HA 310 passed, 2 skipped; npm run build зелёный, dist не
изменился (продуктовый код не тронут); mutation-gate --check применяет
все восемь якорей. Полный прогон новых мутантов — следующим шагом.
Issue: #430
User-Visible: no
Проверка `assertSupportBundleOwnership` бросала при
`initialViewGzipBytes >= 291046` — «граф не стал больше, чем на момент
закрытия #423». Запас до порога: пятнадцать байт на бете 1.71.0, сто
четыре сейчас. Пятнадцать байт gzip меньше одной строки локали, поэтому
первый же посторонний коммит получил бы красный CI с сообщением про
копирайт формы поддержки — к которому не имеет отношения.
«Граф не вырос» — критерий приёмки на момент задачи, а не свойство
продукта. Свойство продукта охраняют `INITIAL_VIEW_GZIP_BUDGET` (300000)
и предупреждение о низком запасе: они судят размер целиком и не
привязаны к чужому номеру issue.
Долговечная половина того же контракта — владение графом (маркеры формы
отсутствуют в initial View, присутствуют в ленивом редакторе) — осталась
и по-прежнему падает в обе стороны. Новый тест закрепляет, что размер в
эту функцию вернуться не может: 0, 291046, 10 МБ и `undefined` проходят
одинаково, пока владение соблюдено.
Гейты: typecheck зелёный, npm test 1787/1786 pass 0 fail, npm run build
зелёный (dist побайтово не изменился — продуктовый код не тронут),
node scripts/bundle-budget.mjs зелёный: initial View 290942 Б, бюджет
300000 Б, запас 9058 Б плюс честное предупреждение о запасе.
Issue: #429
User-Visible: no