# CODE-REVIEW-390-r1 ## Скоуп Issue #390, коммит `cbf5cc1b` «fix: type the five any that slipped past the gate». Класс A (`src/**`) плюс сгенерированное (`dist/**`, `custom_components/houseplan/frontend/**`). **Оговорка о независимости.** Это саморевью по прямому указанию владельца: автор и ревьюер — одна сессия. По PROCESS.md §6 ревьюер обязан быть свежей сессией без контекста реализации; здесь это условие не выполнено, и вес вердикта соответственно ниже. Независимое ревью остаётся доступным. ## Что проверено | Проверка | Результат | |---|---| | `npm run typecheck` | зелёный | | `npm test` | 1635 pass, 0 fail | | `npm run build`, `bundle-sync`, `bundle-tree` | 8 ассетов, три копии совпадают | | `npm run bundle:budget` | 281785 B gzip при бюджете 300000, запас 18215 | | `node scripts/no-new-any.mjs` по диапазону | чисто | | Job «Фронтенд» в CI, прогон #2140 | success | Не прогонялись: golden (в песочнице Chromium 149 против закреплённого 151), браузерные смоки, backend-харнесс. В CI на этом коммите смоки и фронтенд зелёные. ## Находки ### Low-1. Утверждение в сообщении коммита неверно Коммит утверждает: «Поведение не меняется: аннотации типов стираются, эмит тот же». Первая половина верна, вторая — нет. Сверка проведена побайтово: собранные деревья `cbf5cc1b^` и `cbf5cc1b` нормализованы (отпечаток исходников и content-hash в именах чанков заменены на константы) и сравнены. Семь чанков из восьми совпали. Восьмой, `houseplan-editor-runtime`, разошёлся в одном месте: ``` до : const e=Array.isArray(s?.identifiers?.[0])?s.identifiers[0][0]:null; пос: const e=s?.identifiers?.[0],i=Array.isArray(e)?e[0]:null; ``` Причина — введённая мной временная переменная в `_registryIntegrations()`. Эмит изменился, потому что изменился код, а не только типы. **Поведение при этом тождественно.** Исходный вариант вычисляет `identifiers[0]` дважды: под optional chaining в проверке и без него при чтении. Второе обращение безопасно ровно тогда, когда `Array.isArray` вернул `true`. Мой вариант вычисляет один раз и читает то же значение. Разница видна только при геттере с побочным эффектом, чего в реестре HA нет — это словарь данных из MQTT/WS. **Решение ревьюера: снято с записью.** Переписывать код ради побайтового совпадения эмита бессмысленно; исправляется утверждение, а не код. Запись здесь и есть исправление. Урок общий: «эмит тот же» — это измеряемое утверждение, и мерить его надо до того, как оно попадёт в сообщение коммита, а не после. ## Golden: восемь разошедшихся сцен — не из этого коммита На прогоне #2140 (`cbf5cc1b`) golden показала восемь `different`, все device-inbox / device-dialog / toggle-entity. Проверено, что коммит к ним непричастен: 1. **История прогонов.** Golden последний раз была зелёной на #2090. Дальше #2094 и #2110 показывали `different` по `furniture-transform-light/dark` (работа #383), затем девять прогонов подряд golden пропускалась, и первым исполнением после паузы стал #2140. То есть восемь сцен разошлись где-то внутри паузы, а не на моём коммите. 2. **Причина видна в диффах.** `2e30daa3` («test: include device history icons in demo harness») меняет `demo/srv/assets/icons.js` — набор иконок самого стенда, с которого снимаются кадры. `bdf81fad` добавляет UI истории позиций в диалог устройства. Обе работы задевают ровно те поверхности, что в списке. 3. **Мой дифф их задеть не мог.** Единственное изменение эмита — временная переменная внутри `_registryIntegrations()`, функции, которая собирает список интеграций для фильтра. Она не участвует в отрисовке лотка, диалога устройства и попапа цвета — а они в списке разошедшихся. Приёмка этих восьми — отдельное решение владельца по артефакту CI, к #390 отношения не имеет. ## Продуктовый вопрос Пользователь не увидит ничего: изменение типовое, тексты, разметка и логика те же. `User-Visible: no` проставлен верно. ## Вердикт **Вердикт: зелёный · цикл r1/4 · High: 0 · Medium: 0 · Low: 1 → снята с записью · Документ: docs/reviews/CODE-REVIEW-390-r1.md** С оговоркой о независимости из первого раздела.