Files
houseplan-card/docs/reviews/SPEC-REVIEW-362-r1.md
T
2026-08-29 06:14:17 +00:00

118 lines
12 KiB
Markdown

# SPEC-REVIEW-362-r1
Issue: #362 — «Редактор подложки: устройства перехватывают hover и действия инструментов»
Трек: `small` (лёгкий) · заход r1 · блокирующих циклов ревью ТЗ 0 из 2
Материал: тело issue #362 на момент разбора (createdAt/updatedAt 2026-08-29), последний
комментарий автора «ТЗ готово, передача на ревью (small track, r1)».
Дерево кода: ветка `dev`, HEAD `156be645` (issue ещё не в коде — ревью читает только
описание поведения и сверяет его с фактическим состоянием `dev`, из которого фикс будет
писаться).
## Скоуп
Баг в редакторе Background (Подложка): видимая часть device layer (иконка, 44px
псевдо-область, shell/frame, капсула, value/LQI/бейджи, пульсы, спутники
opening-lock) остаётся pointer target и общими device-обработчиками, хотя по
`docs/DECOR-EDITOR.md` устройства в Background — чисто контекстный ориентир на
35% opacity. Из-за этого устройство перехватывает hover/tooltip, может открыть
карточку, выполнить toggle/run или начать drag устройства вместо действия
активного инструмента Подложки (Line/Rectangle/Text/Furniture), и мешает
поставить декоративный элемент в нужную точку.
Продуктовая рамка (`docs/SCOPE.md`): задача в J4 («GUI-only редактирование
плана») и J6 («drag/resize, два редактора») — восстанавливает уже
задокументированный (не новый) UX-контракт Background, а также защищает
инвариант «никакой lock/alarm-экшен не срабатывает по тапу на плане» той же
секции SCOPE.md, поскольку сейчас клик по устройству в Подложке может
инициировать реальный HA-service-call. Правка входит в скоуп, новой персоны или
поверхности не создаёт.
## Как проверялось
Ревью ТЗ на `small`-треке не предполагает файла в `docs/specs/` — проверено,
файла `docs/specs/362-*.md` в дереве нет, как и требуется §5 PROCESS.md.
Каждое техническое утверждение ТЗ («Подтверждение по коду») сверено чтением
актуального `dev` (`156be645`), а не принято на слово автора:
| Утверждение ТЗ | Файл : строка | Результат сверки |
|---|---|---|
| `.stage.mode-decor .devlayer { pointer-events: none }` уже есть, но перекрывается | `src/styles/plan.styles.ts:856` | подтверждено — правило есть |
| `.dev`, `.dev::before`, `.device-shell-frame` явно `pointer-events: auto` | `src/styles/devices.styles.ts:144,182,213` | подтверждено дословно |
| `_renderDevice()` вешает click/contextmenu/pointerover/pointermove/pointerdown/up без учёта Background | `src/houseplan-card.ts:12017-12029` | подтверждено; `role`/`tabindex` действительно ограничены `interactive = view\|devices` (:11980, :12012-13) |
| `_clickDevice()` блокирует только `plan`, Background проваливается в общий action-путь | `src/houseplan-card.ts:5179-5182` | подтверждено — только `if (this._mode === 'plan') return;`, ветки `devices` и общий tap-action идут дальше |
| `_pointerDown()` блокирует `plan`, отдельно обрабатывает `view`, всё остальное (включая Background) идёт в drag-путь устройства | `src/houseplan-card.ts:6337-6361` | подтверждено — после `plan`/`view` сразу `this._drag = {...}` |
| `_showTip()` не проверяет режим вовсе | `src/houseplan-card.ts:6549-6561` | подтверждено — единственная проверка `hoverEnabled`/`_drag`, режим не участвует |
| `_ctxDevice()` (правый клик) уже fail-closed вне View | `src/houseplan-card.ts:5170-5177` | **не баг** — `if (this._mode !== 'view') return;` уже безопасен для Background; ТЗ верно требует лишь сохранить это регрессионной проверкой, не выдаёт это за новый фикс |
| `_selId` (device selection) выставляется только в `_pointerUp` при `moved` | `src/houseplan-card.ts:6388-6396` (единственное присваивание — `houseplan-card.ts:6394`) | подтверждено — фикс `_pointerDown` автоматически закрывает и «device selection» без отдельного гварда |
| `:hover`-покраска CSS не зависит от JS-обработчиков, а только от того, является ли элемент pointer target | `src/styles/devices.styles.ts:329-407` (`:host([data-pointer-hover]) .dev:hover` и т. п.) | подтверждено — это обосновывает контрактный пункт 6 («CSS явно перекрывает descendants, JS-гварды одни pointer-events не заменяют») как техническую необходимость, а не избыточность |
| Терминология «Подложка» / «Просмотр» / «Устройства» | `docs/USER-GUIDE.ru.md:56,109,120,198,1090` и др. | совпадает с интерфейсом, не изобретена |
| Ссылки на дубликаты #101, #213, #266 | `gh issue view 101/213/266` | #213 — расширение hover/action до всей капсулы (источник «капсулы» в ТЗ), #266 — чисто структурный CSS-рефакторинг, #101 — переход View↔редакторы/фон; ни один не дублирует маршрутизацию событий Background, ссылки корректны |
| `demo/smoke_decor.mjs` существует, есть куда добавлять AC1/AC4 | `ls demo/smoke_decor.mjs` | подтверждено |
Гейты кода (typecheck/test/build/смоки) не запускались: на этапе ревью ТЗ кода
ещё нет, проверке подлежит только описание поведения и его соответствие
текущему `dev`.
## Находки
Нет. High — 0, Medium — 0, Low — 0.
Рассмотренные и отклонённые кандидаты на находку:
- *AC5 не называет конкретные файлы existing device/decor smokes.* Отклонено:
AC5 — регрессионная, а не основная проверка; ТЗ явно допускает альтернативное
доказательство «чтение mode guards на код-ревью», выбор конкретных смоков —
техническое решение реализации, не продуктовый вопрос, ревьюер не вправе его
требовать на этом треке.
- *Контракт требует сохранить fail-closed для `_ctxDevice`, хотя эта ветка уже
безопасна.* Отклонено как находка: ТЗ не выдаёт существующее поведение за
новый фикс, а включает его в перечень side-effects, которые обязана держать
регрессия — это корректная практика, не догадка.
- *Не назван отдельный вопрос владельцу.* Отклонено: ни один открытый пункт не
требует продуктового решения (что видит/делает человек) — контракт нигде не
меняет видимое поведение сверх уже описанного в `DECOR-EDITOR.md` (35%
opacity, устройства вне magnet targets), только устраняет утечку
интерактивности. Догадок, выданных за факт, не обнаружено — каждое
поведенческое утверждение либо сверено с кодом (таблица выше), либо с
`DECOR-EDITOR.md`/`UX-MODES.md`.
## Что проверено и корректно
- Обязательные разделы лёгкого трека (§5 PROCESS.md): Проблема · Контракт ·
AC1…AC6 с указанным способом доказательства · Откат — все присутствуют в теле
issue.
- Каждый AC — проверяемое утверждение с явным способом доказательства
(`smoke_decor.mjs`, browser smoke со spies/counters, DOM/computed-style
smoke, diff review + typecheck/unit/build). AC3 отдельно требует, чтобы
мутация (возврат `pointer-events:auto` или снятие mode guard) роняла smoke —
дисциплина «тест умеет падать» заложена уже в ТЗ.
- Скоуп: одна поверхность (Background), нет миграции конфига, нет новых
compatibility/i18n-полей, нет влияния на touch-контракт и performance —
критерии `small` (§5 PROCESS.md) выполнены одновременно, трек выбран верно.
- Контракт не вводит новый UX, а восстанавливает уже задокументированный в
`docs/DECOR-EDITOR.md` (35% opacity, устройства исключены из magnet targets)
и `docs/UX-MODES.md` (Background — desktop-first editor, View/Devices не
меняются).
- Откат описан конкретно и с явным запретом частичного отката (CSS без guards
или наоборот), что верно устраняет риск оставить один из двух путей
случайного действия.
- Технические утверждения о текущем состоянии кода все подтверждены прямым
чтением `dev`@`156be645` (таблица выше) — ни одно не оказалось устаревшим или
неточным.
## Чего не проверял
- Сам фикс не написан — код-ревью впереди, эта проверка не про реализацию, а
про план работы.
- Не проверялись гейты (`typecheck`/`test`/`build`/смоки) — на этапе ТЗ кода
нет, гонять их не над чем.
- Не проверялся live browser-стенд — вне скоупа ревью ТЗ.
## Вердикт
Зелёный. ТЗ полное, однозначное, каждый AC проверяем и привязан к способу
доказательства, технические утверждения подтверждены чтением кода, догадок,
выданных за факт, не найдено. Продуктовых вопросов владельцу нет и не
требуется.