15 KiB
CODE-REVIEW-488-r1
Issue: #488 — панель
/houseplan пуста в реальном HA (стейдж 0 px + hass не доходит до карточки
при первом заходе).
Заход: r1 · блокирующих циклов израсходовано 0 из 2 (лимит §4 для лёгкого
трека — 2 цикла ревью ТЗ; для код-ревью — общий лимит 4, см. §4).
SHA материала ревью: 2b2fc944 (проверено git rev-parse HEAD
непосредственно перед вынесением вердикта — совпадает с SHA зелёного Validate,
на который ссылается заказчик ревью:
https://github.com/Matysh/houseplan-card/actions/runs/34198029189).
Диапазон: git diff origin/dev...HEAD, 4 коммита
(471527c4 fix, 28772133 test, e97e5681 docs, 2b2fc944 docs).
Скоуп
Диф правит два независимых, но совместно найденных дефекта монтирования кастомной панели HA:
- Стейдж 0 px.
<ha-panel-custom>— блок без собственной высоты;:hostсheight:100%резолвится вauto, вся цепочка схлопывается. Правка берёт высоту от viewport:100vh, затемcalc(100dvh − safe-area-inset-top − safe-area-inset-bottom). hassне доходит до карточки при первом открытии (второй, ранее не описанный в теле issue корень — см. «Не заявлено в issue, но обосновано» ниже). HA присваиваетpanel/hass/narrow/routeэлементу сразу послеloadмодуля, до того как top-levelawait import('./houseplan-card.js')даст классу определиться; значения оседают как собственные data-свойства инстанса и затеняют accessors._adoptPreUpgradeProperties()в конструкторе иconnectedCallbackпереносит их через accessors.
Правки: src/houseplan-panel.ts (+27/−… строк), новый сценарий в
demo/smoke_houseplan_panel.mjs (реальный порядок монтирования HA + хост без
высоты), 2 мутанта в scripts/mutation-gate.mjs, обновлённый
test/houseplan-panel.test.mjs, docs/specs/486-house-plan-panel.md,
CHANGELOG.md/CHANGELOG.ru.md, пересобранный dist/** +
custom_components/houseplan/frontend/**, обновлённый
docs/images/screenshots.json (только provenance, не сами PNG).
Не заявлено в issue, но обосновано. Тело issue в разделе «Причина» называет
только высоту; про затенение accessors — ни слова, хотя раздел «Симптом» прямо
описывает два состояния («первый заход — пустая шапка панели, ни заголовка
карточки, ни плана» и «после возврата — шапка карточки есть, стейдж всё равно
пуст») и сам помечает первое как догадку («вероятно, вуаль… проверить в
исправлении»). Автор нашёл настоящую причину первого состояния (свойства
затенены, hass физически не долетает до карточки), задокументировал её в
docs/specs/486-house-plan-panel.md тем же коммитом и покрыл тестом и
мутантом. Это не расширение скоупа: раздел «Приёмка» issue требует «план виден
с первого захода» — без фикса затенения это требование не выполняется одной
только высотой. Отношу к находкам не считаю; это корректное закрытие AC, а не
самодеятельность помимо задачи.
Как проверялось
Дешёвые гейты не перегонялись бы (Validate на этом SHA зелёный), но диф
трогает src/** и заявляет два защитных контракта дорогим гейтом (смок) —
это явно требует мутанта и его прогона (§2.7), а «дешёвые» пришлось прогнать
самостоятельно, т.к. npm run bundle:sync понадобился для смока (класс D
пересобирается, но не входит в Validate из этого списка проверок отдельно).
| Гейт | Прогнан | Результат |
|---|---|---|
npx tsc --noEmit |
да (внутри npm run build) |
0 ошибок |
npm run build + npm run bundle:sync |
да | dist ⟷ custom_components/.../frontend ⟷ demo/srv/assets — cmp всех трёх пар без вывода (идентичны) |
npm test |
да | tests 2248, pass 2247, fail 0, skipped 1 (пропуск — приватная фикстура #281, к делу не относится) |
node scripts/check-docs.mjs |
да (diff трогает src/**) |
Documentation checks passed (7 files, 12 external links) |
node scripts/no-new-any.mjs --base origin/dev --head HEAD |
да | Новых any нет (26 добавленных строк в 1 файле) |
node scripts/smoke-select.mjs --base origin/dev --head HEAD |
да | Вывод: НЕОПРЕДЕЛЁННОСТЬ — символы PRE_UPGRADE_PROPERTIES, _adoptPreUpgradeProperties не зарегистрированы ни в одном смоке (инструмент не знает про добавленный в этом же диффе demo/smoke_houseplan_panel.mjs). Прямое соответствие «правка src/houseplan-panel.ts → смок demo/smoke_houseplan_panel.mjs» очевидно и без инструмента: единственный смок про панель — этот, и диф его же и расширяет. Прогнан. |
node demo/smoke_houseplan_panel.mjs |
да | все 63 проверки true/OK, включая новые haSequence* и autoHeightHost* |
node scripts/mutation-gate.mjs --id=panel-ignores-pre-upgrade-properties |
да | panel-ignores-pre-upgrade-properties: тест покраснел, как обязан (1/1) |
node scripts/mutation-gate.mjs --id=panel-host-height-from-parent |
да | panel-host-height-from-parent: тест покраснел, как обязан (1/1) |
npm run golden:verify |
да (diff меняет CSS-высоту хоста панели, видимый результат под вопросом) | 60/60 passed, 0 упавших; без панель-специфичного baseline (харнес и так фиксирует высоту хоста — подтверждено чтением demo/golden/) |
python -m pytest tests_backend -q |
нет | diff не трогает custom_components/houseplan/**/*.py |
npm run invariants / model-invariants |
нет | diff не трогает геометрию комнат, layout, marker.space, open_spans — панель это HTML-обвязка вокруг уже существующей карточки |
| performance-профили | нет | не названы в AC, diff не касается чувствительных к перфу путей |
Таблица «чем краснеет» (§2.7, защитные AC)
| AC/защита | Чем доказан | Чем краснеет |
|---|---|---|
Хост панели берёт высоту от viewport, а не от <ha-panel-custom> (иначе стейдж 0 px) |
demo/smoke_houseplan_panel.mjs → autoHeightHostFillsViewportMinusInsets, autoHeightHostKeepsPositiveStage (оба true в реальном прогоне) |
node scripts/mutation-gate.mjs --id=panel-host-height-from-parent — мутант возвращает height:100%, смок краснеет (подтверждено прогоном) |
Собственные pre-upgrade свойства (panel/hass/narrow/route) переносятся через accessors, hass доходит до карточки уже на первом открытии |
demo/smoke_houseplan_panel.mjs → haSequenceLeavesNoShadowingOwnProperties, haSequenceForwardsInitialHass, haSequenceForwardsLaterHass, haSequenceAdoptsNarrowThroughAccessor, haSequenceAdoptsRouteAndPanel (все true) |
node scripts/mutation-gate.mjs --id=panel-ignores-pre-upgrade-properties — мутант убирает перенос, смок краснеет (подтверждено прогоном) |
| Существующие golden-панели не меняются | npm run golden:verify |
не мутировалось намеренно; регресс проявился бы падением одного из 60 сценариев — их нет |
Находки
Нет ни High, ни Medium, ни Low.
Разобрано и не вызвало вопросов (детали — «Как проверялось» и код-чтение):
- Порядок вызова
_adoptPreUpgradeProperties()в массиве['panel','hass','narrow','route']не влияет на корректность:hass-setter вызывает_ensureShell()и не зависит от того, установлены ли ужеnarrow/route, а их setters не имеют побочных эффектов на карточку. Проверено чтением, подтверждено прогоном смока (haSequenceAdopts*всеtrue). - Второй вызов
_adoptPreUpgradeProperties()вconnectedCallback— не находка: после апгрейда собственных свойств уже нет (hasOwnPropertyложный), вызов становится no-op. Не вредит, просто defensive. deleteна собственных data-свойствах, выставленных простым присваиванием до апгрейда элемента — стандартно configurable/writable,deleteне может бросить исключение. Проверено чтением.- Регэксп-проверки в
test/houseplan-panel.test.mjs(assert.doesNotMatch(panel, /:host \{[^}]*height: 100%;/) корректно ограничены блоком:host{...}классом символов[^}]*— не цепляют.page { height: 100%; }ниже по файлу. Проверено чтением; это вспомогательные регресс-тесты поверх поведенческого смока, а не единственное доказательство. - Трейлеры коммитов:
Issue: #488во всех четырёх,User-Visible: yesтолько в продуктовом коммите471527c4(единственном, меняющем видимое поведение), с правками в оба changelog в этом же коммите; остальные три —User-Visible: no(тест/доки), корректно. - Классы изменений (
AGENTS.md) соблюдены: A —src/houseplan-panel.ts; B —demo/**,scripts/mutation-gate.mjs,test/**, все со ссылкой на #488; C —docs/**; D —dist/**,custom_components/houseplan/frontend/**меняются только вслед за источником, не сами по себе. docs/images/screenshots.json: диф — толькоsourceFingerprint/sourceSha256,imageSha256не менялся ни в одной записи; согласуется сcheck-docs.mjsзелёным и с коммитом2b2fc944, прямо объясняющим, почему картинки не перевыпускались.- Продуктовая рамка (
docs/SCOPE.md): правка закрывает J1 («show the whole home… live spatial overview») в самом буквальном виде — без неё вход в продукт через боковую панель не работает вовсе. В скоуп, приоритет P1 оправдан.
Чего не проверял
- Реальный HA-инстанс (только Playwright-эмуляция через двойник
<ha-panel-custom>вdemo/smoke_houseplan_panel.mjs, включая порядок вызовов_createPanel/setCustomPanelProperties, который смок воспроизводит текстом, а не импортом настоящего HA frontend). Ручного тестирования в цикле ревью нет по правилам процесса; проверено чтением кода и поведенческим смоком с мутантами, не исполнением на живом HA. python -m pytest tests_backend— не запускал, diff не трогаетcustom_components/houseplan/**/*.py.node scripts/model-invariants.mjs— не запускал, diff не трогает геометрию/layout/marker.space/open_spans.- Полный
ls demo/smoke_*.mjsматрицы кромеsmoke_houseplan_panel.mjs— не прогонял; diff ограничен монтированием панели,smoke-select.mjsне назвал других кандидатов, а тема (панель) больше ни в одном смоке не фигурирует. - Performance-профили — не в AC, не затронуты.
Вердикт
Зелёный. AC issue выполнены и доказаны исполняемым смоком с двумя мутантами, подтверждённо краснеющими; побочный, изначально не описанный в issue корень (затенение accessors) корректно диагностирован, задокументирован и покрыт тем же гейтом; golden не регрессирует; трейлеры и changelog в порядке.
Материал раунда
- Ветка:
issue/488-panel-host, коммит2b2fc94479d8— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
76c6fb8b02386f04bf88c536fdba9c2ab46effbcgit log --all --format='%H %T' | grep 76c6fb8b0238