Первый ночной прогон с честным критерием (#550) дал 726 из 735: девять мутантов
не доходили до заявленного теста. Причины оказались тремя, а не одной, как я
ожидал по трём случаям из #566:
- статически мёртвая ветка (`if (false && …)`, `if (true) throw`) — TypeScript
теряет сужение, сделанное выше: `united`, `_relation`, `previous`, остаток
функции после безусловного throw. Лечится ложью, ложной в рантайме, но не
статически;
- пустой литерал `[]` выводится как `never[]`, и падают уже вызывающие. Лечится
сохранением типа при потере содержимого (`slice(0, 0)`, `as string[]`);
- несовпадение типов в самой замене: `canonicalizeNumber` отдаёт `unknown` там,
где нужен `T`; подмена резолвера литералом сужала union, и ветка
`resolution.reason` становилась `never`; бракованная метка `case` ломала
`switch` по union.
Смысл каждого мутанта сохранён: все девять прогнаны по `--id=` и краснят свой
заявленный тест.
Issue: #569
User-Visible: no
Свидетель, который не готовится к прогону, краснил гейт той задачи, чей дифф его
выбрал, даже когда причина лежала в чужом коммите: на #566 это стоило двух
кругов. Теперь при исходе `setup-failure` в дифф-режиме прогоняется ОПРЕДЕЛЕНИЕ
БАЗЫ на дереве базы, и раннер говорит прямо — отказ предсуществующий или внесён
этим диффом. Сравнение подобного с подобным здесь принципиально: с определением
из головы собственная сломанная правка реестра выглядела бы предсуществующей.
Предсуществующий гейт задачи не красит (решение владельца 14.09) и не теряется:
он назван машиночитаемой строкой и обязан покраснеть в ночном полном прогоне, у
которого есть адресат (#472). Мутанта, которого в базе нет, оправдывать нечем по
построению.
Композиция чтения реестра базы и запуска мутанта живёт отдельным модулем:
границу «запуск не зависит от отбора» держит тест #558, и CLI обязан остаться
тонким.
Issue: #568
User-Visible: no
Ревью r1, Medium: наблюдение о записи на удалённом пространстве говорило
«владелец жив» и про ключ, который ни во что не резолвится. Это заявление о
доказанности там, где её нет: продукт в этом состоянии ничего не удаляет не
потому, что владелец жив, а потому, что не знает — `space-reference-repair`
ведёт `live`, `absent` и `unverified`, три состояния, а не два.
Проверка ведёт те же три. Нерезолвящийся ключ получает наблюдение
`unknown_owner` с той же формулировкой, что и в живом пространстве: «владелец не
найден в конфигурации (возможно устройство HA)». Тест сверяет теперь и ТЕКСТ
причины — вид наблюдения без текста эту асимметрию пропускал, ровно так она и
проехала.
Issue: #566
User-Visible: no
`optimizer-micro-interval-cleanup-disabled` вставлял безусловный `return` в
начало функции. Остаток функции становился недостижимым, и там TypeScript терял
сужение `profile` — компиляция падала до теста. Возврат сделан под
рантайм-условием: смысл мутанта («очистка отключена») тот же, остаток кода для
проверки типов остаётся достижимым.
Issue: #566
User-Visible: no
`inner-span-reads-whole-edge-thickness` и `safe-resize-legacy-midpoint-fail-open`
падали на `npx tsc -p tsconfig.test.json` до запуска заявленного теста и потому
не проверяли ничего. Причина одна: патч делал ветку статически мёртвой
(`if (false)`, `false &&`), а в мёртвой ветке TypeScript теряет сужение типов,
сделанное выше — `profile` снова `| null`, `direct` снова `| undefined`.
Условия заменены на ложные в рантайме, но не статически. Смысл мутантов тот же,
и оба теперь ловятся заявленными тестами. Обнаружено прогоном на этой ветке:
правка реестра затягивает в отбор мутантов чужие файлы, и красным стал гейт
задачи, к которой эти записи отношения не имеют. Провал такого мутанта
незаметен, пока его не выберет дифф — это отдельный пробел, #568.
Issue: #566
User-Visible: no
Инварианты объявляли нарушением ЛЮБУЮ запись layout, чьё пространство удалено.
Продукт так не считает: `space-reference-repair` удаляет такую запись только
когда может доказать, что владелец тоже исчез, и сознательно хранит её, когда
владелец жив или доказательств нет — удаление уносит расстановку пользователя.
Конфиг сразу после Optimize законно содержит такие записи, и проверка называла
нарушением штатное состояние.
Теперь правило то же, что у продукта: нарушение — только когда владельца нет по
самой конфигурации (комната, область или снятый маркер). Остальное —
наблюдение, как у ветки `unknown_owner` рядом.
Issue: #566
User-Visible: no
Гард `keepTwoPoint` решал, сохранять ли двухточечную калибровку, сравнением
`JSON.stringify`, то есть текста. Порядок ключей `sources` при этом меняет сам
билдер: `slots`/`ranges`/`zones`/`occupancy_entity`/`count_entity` он удаляет и
дописывает заново, а `availability_entity` остаётся на месте и уезжает в начало.
Конфиг, только что записанный этим же билдером, при следующем открытии
сравнивался неравным — и радар с `availability_entity` терял `refs` и `rms_cm`,
получая `method: manual`, при первом же обычном сохранении.
Сравнение стало каноничным по порядку ключей объектов и осталось чувствительным
к порядку элементов массивов: позиция слота — это его `target_N`. Проекция не
затронута — она считается от `mount`, `cell_cm` и `mirror`.
Issue: #567
User-Visible: no
Полевая проверка основного View по #560: шесть обезличенных планов корпуса и
цепочка import → Optimize → Optimize → Resize → сохранение с численными
оракулами, семь household-путей с независимыми оракулами в двух ширинах
карточки, аудит доступности с измеренными числами. Продуктовых правок нет:
находки заведены отдельно (#564, #565, #566).
Issue: #560
User-Visible: no