Аудит 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
A fully fictional house (plans, devices, states) used for README screenshots,
the demo GIF and headless smoke tests — so no real home data ever appears in
public materials.
srv/demo.html — self-contained host page: <ha-icon>/<ha-card> stubs and a
fake hass (registries, states, callWS, callService, floors).
srv/assets/ — generated plan SVGs and icons.js (node demo/gen_icons.mjs,
needs the repo's devDependencies). The card bundle is copied from dist/:
npm run bundle:sync (копия стенда не коммитится, #255).
serve.mjs — playwright launcher (route interception, no web server).
smoke_*.mjs — feature smoke tests; run with a Chromium installed via
PLAYWRIGHT_BROWSERS_PATH=<dir> npx playwright install chromium-headless-shell.
Note for sandboxed sessions: /tmp does not survive; this directory is the
persistent home of the harness (docs/DEVELOPMENT.md has the LD_LIBRARY_PATH recipe).