From afba49a3faa8d35ed4e8cb683b1a979d7737bd0c Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Sat, 5 Sep 2026 22:44:26 +0000 Subject: [PATCH] docs: review document for #464 Issue: #464 User-Visible: no --- docs/reviews/CODE-REVIEW-464-r2.md | 153 +++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 docs/reviews/CODE-REVIEW-464-r2.md diff --git a/docs/reviews/CODE-REVIEW-464-r2.md b/docs/reviews/CODE-REVIEW-464-r2.md new file mode 100644 index 00000000..81d43720 --- /dev/null +++ b/docs/reviews/CODE-REVIEW-464-r2.md @@ -0,0 +1,153 @@ +# CODE-REVIEW-464-r2 + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/464 +- **Ветка:** `issue/464-zigbee-topology-layer` +- **Материал:** `git diff origin/dev...HEAD` на SHA `87cab133ac345c2283050bbea27ce73534393794` + (= текущий `HEAD` детач-чекаута ревьюера, совпадение проверено `git rev-parse HEAD`) +- **Трек:** полный (не менялся с r1) +- **Заход:** r2 · блокирующих циклов израсходовано 1 из 4 (потрачен в r1, жёлтым; + зелёный вердикт этого раунда цикл не тратит — §4) + +## Скоуп раунда + +Разбор **по дельте** (PROCESS.md §2.9): предыдущий раунд (`CODE-REVIEW-464-r1.md`, +жёлтый, SHA `276bf193`) закрыт одной находкой High-1. Дельта этого раунда — +`git diff 276bf193..HEAD`, 2 коммита (`0f7b0dff` продукт+тесты+доки, +`87cab133` только `docs/images/screenshots.json`), локальна: правит один CSS-селектор, +расширяет один smoke и добавляет один mutation-gate мутант под уже найденный +дефект. Ребейза на ушедший вперёд `dev` не было, новая подсистема не задета, +контракт поведения не сменился (AC1 из ТЗ не переписан, только доказан) — полный +разбор не требуется, разбираю дельту плюс всё, до чего она дотягивается. + +Файлы дельты (без `dist/**` и `custom_components/houseplan/frontend/**`, +класс D — сверены пересборкой, см. гейты): + +- `src/styles/devices.styles.ts` — продуктовый фикс (класс A) +- `demo/smoke_zigbee_topology_hover.mjs`, `scripts/mutation-gate.mjs` — тесты (класс B) +- `docs/specs/464-zigbee-topology-layer-order.md`, `docs/TESTING.md`, + `docs/CHANGELOG.md`, `docs/CHANGELOG.ru.md`, `docs/images/screenshots.json` — + документация (класс C) + +## Закрытие раунда r1 + +| Находка | Чем закрыта | Где это видно | +|---|---|---| +| **High-1**: реальный курсор мыши над hovered-source endpoint матчит старое правило `:host([data-pointer-hover]) .dev:hover { z-index: 5 }` (специфичность `(0,4,0)`) сильнее нового `.dev[data-hp-zigbee-topology-endpoint] { z-index: 8 }` (специфичность `(0,2,0)`); оверлей стоит на `z-index: 7` → `5 < 7`, линия рисуется поверх маркера-источника | Добавлено правило той же специфичности что и конфликтующее плюс атрибут endpoint плюс `:hover` — `(0,5,0)`, что строго выше `(0,4,0)` и решает каскад в пользу endpoint независимо от порядка объявления | `src/styles/devices.styles.ts:345` — `:host([data-pointer-hover]) .dev[data-hp-zigbee-topology-endpoint]:hover { z-index: 8; }`. Доказано исполнением: smoke добавил блок с настоящим `page.mouse.move` (не `dispatchEvent`) и `result.realPointerEndpointWins` (`demo/smoke_zigbee_topology_hover.mjs:555-579`); лично прогнан `node demo/smoke_zigbee_topology_hover.mjs` → `OK`, `"realPointerEndpointWins": true`. Тест умеет падать: новый мутант `zigbee-topology-hovered-endpoint-elevation-removed` (`scripts/mutation-gate.mjs:6067-6076`) занижает z-index новой строки до 1; лично прогнан `node scripts/mutation-gate.mjs --id=zigbee-topology-hovered-endpoint-elevation-removed` → «покраснел, как обязан» | + +Других находок в r1 не было (Medium: 0). + +## Унаследовано из r1 + +Принято без повторной проверки — делта их не задевает: + +- **AC2 (ownership/lifecycle/single-projection)** и **AC3 (casing неизвестного + LQI)** — доказаны чтением и исполнением в `CODE-REVIEW-464-r1.md` на SHA + `276bf193a2bec40a3c2fc823d364cbda00ac10d5`; дельта r2 не трогает + `hp-zigbee-topology-overlay.ts`, `zigbee-topology-overlay-bridge.ts`, + `houseplan-card.ts` — файлы, где эти AC реализованы. +- **Ревью ТЗ** — `SPEC-REVIEW-464-r1.md`, зелёное, SHA спеки `aae9efb9`. +- **Гейты `typecheck`/`test`/`build`/`bundle`/`no-new-any`/`process-gate`/ + `benchmark:zigbee-topology` на SHA `276bf193`** — зелёный Validate + (run 33994548645), зафиксировано в `CODE-REVIEW-464-r1.md`. +- **i18n, трейлеры структуры коммитов, cleanup-матрица (pointerleave/touch/ + pen/mode/space/setting/disconnect)** — проверены в r1, дельта их не касается. + +## Как проверялось в этом раунде + +| Гейт | Статус | Как | +|---|---|---| +| `typecheck` / `npm test` / `npm run build` + 3 копии бандла | ✅ зелёный, не перегонял отдельно | Validate на точном SHA `87cab133`: https://github.com/Matysh/houseplan-card/actions/runs/33996034405 (success) — «дешёвые гейты на этом SHA уже подтверждены». Дополнительно лично прогнал `npm run bundle:sync` (`tsc --noEmit && rollup -c` внутри) для собственного воспроизведения — зелёный, `git status` после чист: пересборка байт-в-байт совпала с закоммиченным `dist/**`/`custom_components/houseplan/frontend/**` | +| `node scripts/check-docs.mjs` | ✅ зелёный | Прогнал сам: `Documentation checks passed (7 files, 12 external links)`; diff трогает `src/**`, отпечаток скриншотов обязателен — коммит `87cab133` обновляет только `sourceFingerprint`/`sourceSha256`, все `imageSha256` побайтно не изменились (сверено чтением диффа `docs/images/screenshots.json`) | +| `node demo/smoke_zigbee_topology_hover.mjs` (прямое совпадение `smoke-select.mjs`) | ✅ зелёный | Прогнал сам после `bundle:sync`: `OK`, включая новый `realPointerEndpointWins: true` | +| Мутант `zigbee-topology-hovered-endpoint-elevation-removed` | ✅ красный при повреждении, как обязан | Прогнал сам: `node scripts/mutation-gate.mjs --id=zigbee-topology-hovered-endpoint-elevation-removed` → «поймано 1 из 1» | +| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | ✅ выполнен | Прямое совпадение: только `smoke_zigbee_topology_hover.mjs` (уже прогнан выше). 29 слабых связей — все по одному распространённому символу `_mode`; дельта не трогает переключение режимов (только CSS-селектор hover/endpoint и тесты/доки) — решил не гонять, риска регрессии не вижу | +| `npm run golden:verify` | не гонял | В `demo/golden/**` нет Zigbee-topology-сценария (проверено чтением `demo/golden/matrix.mjs` и содержимого `demo/golden/`); дельта не расширяет видимый в golden рендер, а обновлённый скриншот-фингерпринт уже подтверждён `check-docs.mjs` и байт-в-байт сверкой `imageSha256` | +| Остальные 6 из 7 старых мутантов задачи + полный `mutation-gate.mjs --check` | не гонял | Не задеты дельтой (файлы мутантов для AC2/AC3 не менялись); наследую зелёный статус r1/хендоффа автора; полный `--check` — предрелизный объём, несоразмерен точечной правке | +| `npm run invariants` | не гонял | Дельта не трогает геометрию комнат/стен, `layout`, `marker.space`, `open_spans` — только экранный z-index topology-оверлея | +| `python -m pytest tests_backend` | не гонял | Диапазон не трогает `custom_components/**/*.py` | +| `npm run benchmark:zigbee-topology`, `bundle:budget`, `no-new-any`, `process-gate --issues` | не гонял | Не задеты дельтой; зелёный статус наследую из Validate на `87cab133` и хендоффа автора r2 (перечислены поимённо в его комментарии) | + +## Что проверено и корректно + +- **Специфичность CSS разрешает конфликт правильно.** Новое правило + `:host([data-pointer-hover]) .dev[data-hp-zigbee-topology-endpoint]:hover` + весит `(0,5,0)` — строго больше конфликтующего `:host([data-pointer-hover]) + .dev:hover` `(0,4,0)` — независимо от порядка объявления в файле. Проверено + и расчётом, и (сильнее) реальным исполнением через `page.mouse.move`. +- **Нет скрытых конфликтов той же природы.** Перепроверил чтением все + `z-index`-правила `devices.styles.ts` (`grep`) — единственная другая + hover-зависимая пара (`.dev:focus-visible { z-index: 5 }`) реагирует на + клавиатурный фокус, а не на реальный курсор; топология активируется только + реальным mouse-hover (зафиксировано ещё в аналитике issue: «touch и kiosk не + меняются, топология доступна только admin + mouse hover»), так что этот путь + не достижим для endpoint-элементов и не относится к AC1. +- **Мутант защищает именно новую строку**, а не дублирует старый: `find`/ + `replace` бьёт по селектору с `:hover`, отличному от базового + `zigbee-topology-endpoint-elevation-removed`; `id` уникален + (`grep -c` = 1 по всему файлу). +- **Регрессионный smoke использует настоящий курсор**, а не синтетический + `dispatchEvent` — то же средство, которым сам дефект был найден в r1; + ждёт до 5 c появления `:hover` + атрибута + отрисованной линии перед + сравнением `getComputedStyle(...).zIndex`, что исключает гонку с отрисовкой. +- **Документация и трейлеры.** `CHANGELOG.md`/`CHANGELOG.ru.md` в одном + коммите с `User-Visible: yes` (`0f7b0dff`), формулировка «including the one + under the pointer» / «включая маркер прямо под курсором» точно описывает + зафиксированное поведение и не расширяет сам контракт. `docs/TESTING.md` и + ТЗ `docs/specs/464-...md` получили по абзацу, описывающему именно этот + инвариант (реальный `:hover` vs синтетический `pointerover`) — согласуется + с находкой r1 дословно. `87cab133` несёт `User-Visible: no` — корректно, + видимых кадров это не меняет (imageSha256 не тронут). +- **Инварианты модели вне периметра** — дельта не касается геометрии, + подтверждено чтением диффа: единственный продуктовый файл — CSS-стили. + +## Чего не проверял + +- Полный `mutation-gate.mjs --check` (весь проект) и оставшиеся 6 старых + мутантов задачи — несоразмерно точечной правке, не задеты дельтой. +- `npm test` целиком и `npm run build` отдельным прогоном юнит-сьюта — положился + на зелёный Validate точного SHA `87cab133`; лично воспроизвёл только + `tsc --noEmit && rollup -c` через `bundle:sync`. +- 29 смоков со слабой связью по символу `_mode` — не гонял, дельта не трогает + переключение режимов; решение зафиксировано в таблице гейтов. +- Реальное touch/pen устройство за пределами эмуляции `pointerType` — тот же + довод, что и в r1: различие смок/реальное устройство здесь не той природы, + что в закрытой находке (`pointerType` — явное поле события, не завязано на + hit-testing браузера, в отличие от `:hover`). +- Визуальный скриншот итоговой сцены — числовой `getComputedStyle(...).zIndex` + уже является единственным источником, управляющим порядком отрисовки; + дополнительный кадр ничего не добавил бы. + +## Единый источник числа + +Дельта не вводит и не меняет пользователем видимую величину (числовое +значение, дублируемое в двух местах интерфейса) — правится исключительно +порядок отрисовки (`z-index`), самого числа/значения пользователю не +показывается. `test/single-source-numbers.test.mjs` не затрагивается дельтой +(файл не менялся, диапазон не относится к value-badge/подписям). + +## Вывод + +High-1 из r1 закрыт: причина (специфичность CSS `:hover` выше специфичности +endpoint-правила) устранена добавлением правила ещё большей специфичности, +доказано личным исполнением smoke с настоящим курсором и личным исполнением +нового защитного мутанта, который краснеет при откате правки. Других находок +не внесено. AC2/AC3 наследуются из r1 без повторной проверки — дельта их не +касается. Документация и трейлеры в порядке. + +**Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0** + +--- + + + +## Материал раунда + +- Ветка: `issue/464-zigbee-topology-layer`, коммит `87cab133ac34` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `445cc106c18942021aa8209aeac33ac47f1ccbda` + ``` + git log --all --format='%H %T' | grep 445cc106c189 + ``` +- ТЗ `docs/specs/464-zigbee-topology-layer-order.md`, блоб `043ada287e048ed9750080c342b2a2fdb869d9f0` + ``` + git log --all --find-object=043ada287e048ed9750080c342b2a2fdb869d9f0 -- docs/specs/464-zigbee-topology-layer-order.md + ```