Files
houseplan-card/docs/reviews/CODE-REVIEW-212-r2.md
T
2026-08-20 06:22:45 +00:00

13 KiB
Raw Blame History

CODE-REVIEW-212-r2

  • Issue: https://github.com/Matysh/houseplan-card/issues/212
  • Спецификация: docs/specs/212-device-icons-polish.md (зелёный SPEC-REVIEW-212-r1)
  • Диапазон ревью: origin/dev...HEAD. Полная задача — ec6f77b (specify) · 5cae1fd (SPEC-REVIEW doc) · 120d413 (fix: polish device icons and pointer feedback) · b7bfc92 (CODE-REVIEW-212-r1 doc) · c5ba699 (fix: address device icon review findings, Issue: #212, User-Visible: yes). Этот цикл — второй код-ревью, предметно новое по сравнению с r1 — только диапазон b7bfc92..c5ba699.
  • Ветка: issue/212-device-icons-polish
  • Роль: ревьюер кода, свежая сессия без контекста написания ТЗ/кода.

Скоуп проверки

CODE-REVIEW-212-r1 (docs/reviews/CODE-REVIEW-212-r1.md) вынес жёлтый вердикт с двумя находками Medium в скоупе, обе без High:

  • M1 — три существующих regression-смока (smoke_glow.mjs, smoke_room_settings.mjs, smoke_ux_fixes.mjs) остались на старом MouseEvent('mouse{enter,move,leave}')/удалённом _touchSeen и были красными на SHA 120d413 из-за миграции room/marker hover на PointerEvent.
  • M2 — hp-device-preview.ts гейтировал .dev:hover через :host([data-pointer-hover]), но карта не проставляла этот атрибут экземпляру hp-device-preview — hover z-index в Device preview был мёртв навсегда.

Задача этого цикла — проверить, закрыты ли обе находки по существу (не просто «тест больше не падает», а поведение действительно восстановлено), и что фикс не завёл новой регрессии. Весь остальной код задачи (AC1–AC16) не тронут диапазоном b7bfc92..c5ba699 и повторно не пересматривался — он был закрыт по существу в r1.

Как проверялось

Гейт Команда Результат
Типы npx tsc --noEmit зелёный
Юнит npm test 952/952 зелёных
Сборка npm run build + md5sum dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js demo/srv/assets/houseplan-card.js зелёная; все три хэша идентичны (b238775be1735d550f2b95c1e268cfa1)
Документация node scripts/check-docs.mjs --external «Documentation checks passed (7 files, 10 external links)»
Целевые смоки, фиксившиеся в c5ba699 node demo/smoke_glow.mjs, node demo/smoke_room_settings.mjs, node demo/smoke_ux_fixes.mjs, node demo/smoke_device_preview_parity.mjs все 4 зелёные
Failing-before/passing-after для M1 те же 3 смока прогнаны против бандла коммита b7bfc92 (пере-собран в отдельном git worktree, с символической ссылкой на существующий node_modules) smoke_glow → FAILED (3), smoke_room_settings → FAILED (1), smoke_ux_fixes → FAILED (8) — то же множество, что описано в CODE-REVIEW-212-r1
Failing-before/passing-after для M2 новый файл smoke_device_preview_parity.mjs (из HEAD, с ассертом previewHoverGatePropagates) прогнан против бандла b7bfc92 previewHoverGatePropagates: false → FAILED (1) — тест умеет падать именно на старом коде
Смежные названные смоки той же hover-механики (не изменены диффом, но зависят от POINTER_HOVER_TARGET_SELECTOR) node demo/smoke_touch_tips.mjs, node demo/smoke_feedback_v2.mjs, node demo/smoke_help_affordance.mjs, node demo/smoke_device_icon_design.mjs все зелёные — расширение селектора не задело существующие цели
Mutation-proof node scripts/mutation-gate.mjs --check (все зарегистрированные guard'ы) и отдельно node scripts/mutation-gate.mjs --id=device-touch-hover-gate-removed все guard'ы зелёные на чистом коде; device-touch-hover-gate-removed «покраснел, как обязан» на мутанте

Геометрический вывод AC1/AC3 не пересматривался — диапазон b7bfc92..c5ba699 эти файлы не трогает.

Верификация находок r1

M1 — исправлено, доказано реальным поведением, не только тестом

src/houseplan-card.ts не менялся в части обработчиков — миграция на PointerEvent уже стояла в 120d413. Фикс — только в трёх смоках: MouseEvent('mouse{enter,move,leave}') заменён на PointerEvent('pointer{enter,move,leave}', { pointerType: 'mouse' }), а проверка touch-подавления тултипа (smoke_ux_fixes.mjs) перестала писать удалённый статический c.constructor._touchSeen и вместо этого шлёт настоящую последовательность pointerdown/pointermove/pointerup с pointerType: 'touch'.

Прочитано и прослежено по коду: pointerdown на .room всплывает (bubbles: true, composed: true) до @pointerdown на stage-контейнере (src/houseplan-card.ts:15808), который вызывает _notePointer(e) — PointerModalityController переключает модальность в touch до следующего pointermove. Обработчик tip на .room (строка 15907) на pointermove вызывает _showTip → _notePointer(ev) → if (!this._pointerModality.hoverEnabled) return; — при модальности touch _tip не устанавливается. Тест проверяет ровно это: c._tip === null после реального касания, а не факт вызова удалённого статического флага.

Подтверждено экспериментально (не только чтением): все три смока красные на бандле b7bfc92 (та же картина, что зафиксирована в r1) и зелёные на c5ba699. Доказано — воспроизведена как первоначальная регрессия стенда, так и её устранение.

M2 — исправлено, доказано смоком, который умеет упасть

POINTER_HOVER_TARGET_SELECTOR = 'hp-dialog, hp-help, hp-color-opacity, hp-device-preview' — новая единая константа, используемая и в _syncPointerHoverTargets (полный проход по renderRoot.querySelectorAll при смене модальности, подписка на строке 736), и в _syncPointerHoverSubtree (обработчик MutationObserver на добавленные узлы, строка 2017). hp-device-preview рендерится напрямую в шаблоне карты, то есть попадает под оба механизма симметрично hp-dialog/hp-help/ hp-color-opacity, ранее уже подключённым тем же способом. Проверено чтением, не исполнением, в части внутреннего механизма подписки.

Экспериментальная часть: новый ассерт previewHoverGatePropagates в smoke_device_preview_parity.mjs диспатчит настоящий PointerEvent( 'pointerover', { pointerType: 'mouse' }) на <hp-device-preview> и проверяет data-pointer-hover и у карты, и у preview. На c5ba699 — true; при прогоне того же (нового) файла смока против бандла b7bfc92 — false, тест падает. Это прямое доказательство, что ассерт способен детектировать именно тот регресс, который описывала находка M2, а не проверяет тривиально всегда истинное условие.

Юнит-контракт (device-marker-polish-contract.test.mjs) расширен: список файлов naked-hover-selector сканирования теперь включает hp-device-preview.ts, и отдельный regex требует, чтобы POINTER_HOVER_TARGET_SELECTOR содержал hp-device-preview. Доказано.

Проверено и корректно

  • Оба Medium из r1 закрыты по существу, не косметически: поведение восстановлено (подтверждено сравнением бандлов до/после фикса), а не просто подогнан ассерт под текущий результат.
  • Три копии бандла идентичны; трейлеры c5ba699 корректны (Issue: #212, User-Visible: yes); оба changelog (docs/CHANGELOG.md, docs/CHANGELOG.ru.md) обновлены в том же коммите и формулировка («Device preview» — термин уже используется в этом написании в docs/specs/212-device-icons-polish.md §5/§13, не изобретён заново).
  • docs/images/screenshots.json — обновлён только sourceFingerprint/ sourceSha256 (бандл изменился), imageSha256 не менялся — визуальный результат скриншотов не затронут точечным фиксом, check-docs.mjs подтверждает согласованность.
  • Расширение POINTER_HOVER_TARGET_SELECTOR не задело существующие цели гейта (hp-dialog, hp-help, hp-color-opacity) — смежные смоки (touch_tips, feedback_v2, help_affordance, device_icon_design) зелёные без изменений.
  • Diff b7bfc92..c5ba699 не расширяет скоуп: изменены только файлы, напрямую относящиеся к M1/M2 (1 продуктовый файл, 4 смока, 1 юнит-тест, 2 changelog, 1 fingerprint-файл) — без побочных правок «раз уж я здесь».
  • i18n и backend (custom_components/**/*.py) не затронуты этим диапазоном.

Чего не проверял

  • Полный demo/smoke_*.mjs (127 файлов) не прогонялся — диапазон b7bfc92..c5ba699 узко точечный (M1/M2), прогнаны все 4 напрямую изменённых смока плюс 4 смежных, зависящих от того же POINTER_HOVER_TARGET_SELECTOR/PointerModalityController. Остальные AC1–AC16-смоки уже прогонялись и разбирались в CODE-REVIEW-212-r1 и этим диапазоном не затронуты — повторный прогон не добавил бы информации.
  • npm run golden:verify/golden:accept не запускался в этом цикле — диапазон не меняет геометрию/разметку, только тест-стенд и подписку на DOM-атрибут; визуальный результат (см. imageSha256 выше) не изменился. Финальная сверка на Linux CI — пре-релизный гейт (PROCESS.md §8, §13).
  • performance_smoke, python -m pytest tests_backend, npm run inventory не запускались — вне гейта код-ревью (PROCESS.md §8) и диапазон не трогает Python/производительность.
  • Геометрический вывод AC1/AC3 и полная AC-трассировка AC1–AC16 не пересматривались повторно — они не входят в диапазон этого цикла и были закрыты по существу в CODE-REVIEW-212-r1.

Вердикт

Обе находки r1 закрыты по существу и подтверждены экспериментально — демонстрацией, что тот же тест/смок красный на коде до фикса и зелёный после, а не только чтением итогового кода. Новых High или Medium в этом диапазоне не найдено; диапазон узкий и не расширяет скоуп задачи.

Вердикт: зелёный · цикл r2/4 · High: 0 · Medium: 0 → в задаче