Files
houseplan-card/docs/reviews/SPEC-REVIEW-578-r1.md
T
2026-09-14 18:06:47 +00:00

192 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-578-r1
- Issue: #578 — «Touch: второй палец pinch на устройстве запускает long-press и HA more-info»
- Этап: spec (PROCESS.md §2.4), заход r1, блокирующих циклов израсходовано 0 из 4
- Материал: тело issue #578, раздел `## ТЗ` (сборка 14 разделов, план автотестов, принятые предположения), состояние `S4-spec-review`, комментариев к issue нет — это первый заход
- Проверено на `dev` @ `ad4000f95a152d2cdd0fc41f3cb1707df121a47e` (рабочая копия уже на этом SHA)
## Скоуп ревью
Ревью ТЗ, не кода: продукт ещё не изменён. Вопрос — выполнимо ли и однозначно ли
ТЗ, доказуем ли каждый AC, не выдана ли догадка за факт, укладывается ли трек
(полный) и не требуется ли продуктовый вопрос владельцу.
## Как проверялось
1. Прочитан `docs/SCOPE.md` — задача чинит нарушение touch-контракта в View/kiosk
(персоны «Household members», «Guests/kiosk», §«View mode is the product»);
лежит внутри уже принятой рамки, нового job не открывает и не расширяет её.
2. Прочитаны `AGENTS.md` и разделы PROCESS.md §2.4, §2.9/2.10 (не применимо — r1),
§4, §7.1, §7.2.
3. Прочитано тело issue #578 целиком (`## Проблема` → `## ТЗ`, 14 разделов).
Комментариев нет.
4. Прочитан `docs/TOUCH-SUPPORT.md` (контракт View/kiosk, разделы «Product
contract», «What "fully supported View" means», «Safety floor»).
5. Прочитан `docs/USER-GUIDE.ru.md` (табл. §на строках 1009/1132/1133/1147) —
термины «долгое нажатие, 600 мс», «внутренняя карточка House Plan», «HA
more-info», «правый клик» использованы в ТЗ так же, как в интерфейсной
документации; не изобретены.
6. **Технический разбор аналитики сверен с кодом на названном SHA**, а не принят
на веру (обе претензии автора — не догадка):
- `src/houseplan-card.ts:7126-7146` (`_pointerDown`, ветка `view`):
`_holdTimer` действительно взводится безусловно, без проверки
`_touchSequenceMultitouch`/`_touchClickGuard.sequenceMultitouch` —
соответствует претензии «второй палец на маркере заново вооружает
long-press».
- Capture-guard `_touchGestureGuard` (`src/houseplan-card.ts:2090-2093`)
подписан только на `pointerdown/pointermove/pointerup/pointercancel/
lostpointercapture/click` (`:11451-11456`); `@contextmenu` навешен прямо на
маркер (`:12555`) и вызывает `_ctxDevice()` (`:5751-5759`) в обход
capture-фазы — соответствует претензии «touch contextmenu обходит защиту
pinch».
- `src/touch-gesture-click-guard.ts` (класс `TouchGestureClickGuard`,
проверен целиком) и `test/touch-gesture-click-guard.test.mjs` показывают,
что существующий guard #563 закрывает только `click`, не имеет понятия о
long-press/contextmenu, и уже сейчас корректно снимает блокировку на любой
новый `pointerDown` иного `pointerType` (тест «a new mouse sequence on a
hybrid device is not held by an old pinch», строки 50-63) — то есть
примитив, который ТЗ предлагает обобщить (§14), уже имеет нужное свойство
для гибридного случая, и его расширение с высокой вероятностью наследует
это свойство бесплатно.
- `_keyDevice` (`:5903-5908`) подтверждает существование клавиатурной
активации Enter/Space, на которую ссылается контракт п.9 раздела 5 — не
придуманная фича.
7. Сверены названные в плане автотестов файлы/инструменты:
`demo/smoke_editor_gestures.mjs`, `demo/smoke_long_press_gesture.mjs`,
`scripts/mutation-gate.mjs`, `docs/TESTING.md` (раздел про
`scripts/mutation-registry.mjs`) — все существуют, инфраструктура для
AC8/AC9 реальна, а не гипотетична.
8. Проверено согласование трека: у `small` (PROCESS.md §5) требуется ровно одна
поверхность, отсутствие нового UX-контракта и touch-эффекта — задача правит
`pointerdown`/long-press/`contextmenu`/click одновременно и меняет
`docs/TOUCH-SUPPORT.md`, то есть полный трек выбран верно, автор обосновал
это явно, а не промолчал.
Код не менялся, гейты (`typecheck`/`test`/`build`) на этой стадии не
прогонялись — они относятся к этапу code-review, спецификация не трогает `src/**`.
## Находки
### Low-1 — i18n закрыт только косвенно
`§7.1` требует отдельно отраслью i18n. В ТЗ единственное упоминание — пункт
«вне scope»: «изменение конфигурации, backend/WebSocket протокола, HA
entity/device model **либо i18n**» (issue, раздел «4. Вне scope»). Явного
«новых ключей нет» как отдельного вывода не сформулировано.
По существу пункт закрыт: раздел «7. UX и доступность» прямо запрещает любые
новые сообщения/тосты/overlay при pinch, а вся правка — про подавление событий,
не про текст интерфейса. Второй независимый источник (перечень «Затронутые
модули», раздел 10) не содержит ни одного i18n-файла. Открытого вопроса это не
создаёт.
**Решение ревьюера:** Low, снимается без правки — вывод «новых i18n-ключей нет»
следует из §7 и §10 однозначно, дублировать его отдельной строкой не обязательно
для однозначности исполнения.
### Low-2 — сценарий «новый mouse pointerdown сразу после pinch» не назван явно ни в одном AC
Контракт п.8 раздела 5 (второе предложение): «Mouse pointerdown новой
последовательности на гибридном устройстве не наследует блокировку
закончившегося pinch». Ни один AC (AC1–AC9) не называет этот сценарий по имени;
ближе всего AC6 («сохраняет mouse click, правый клик и Enter/Space без ложного
подавления») и общая фраза плана автотестов п.4 («сохранить контрольные
позитивные сценарии... mouse right-click»), но явного «мышь сразу после
завершённого pinch» там нет.
Смягчающее обстоятельство, подтверждённое чтением кода (см. выше, п.6): для уже
существующего click-guard это свойство уже реализовано и покрыто тестом
`test/touch-gesture-click-guard.test.mjs:50-63`, а обобщение того же примитива
на long-press/contextmenu (что и предлагает §14 ТЗ) с высокой вероятностью
наследует то же поведение той же веткой кода (`if
(this._activeTouchPointers.size === 0) this._postGestureClickBlocked = false;`
не зависит от `pointerType`). Риск регрессии этого конкретного угла невысок, но
он не нулевой, если реализация заведёт для long-press/contextmenu отдельный,
не переиспользованный примитив.
**Решение ревьюера:** Low, не блокирует. Рекомендация автору (не обязательна к
исполнению до кода): при реализации явно включить проверку «мышиный
`pointerdown` сразу после конца pinch не наследует блокировку» в
unit/browser-доказательство AC6 или AC7, раз уж контракт её формулирует.
Формально снимаю с записью — General wording AC6 достаточно для однозначности
приёмки, кодревьюер вправе потребовать явный тест-кейс при реализации.
Ни High, ни Medium-находок нет.
## Что проверено и корректно
- Обязательные разделы §7.1 присутствуют все: сценарий и «что человек увидит»
(раздел 1), проблема (2), скоуп/не-скоуп (3–4), контракт поведения (5),
данные/совместимость/миграция (6), UX (7), AC1–AC9 с указанным способом
доказательства (8), план автотестов (9), риски и откат (12), release-артефакты
(13). Плюс необязательные, но полезные «затронутые модули» (10) и
«производительность/touch-impact» (11).
- Каждый AC имеет наблюдаемый oracle (счётчики `_infoCard`/`hass-more-info`/
confirmation/service, факт изменения zoom, число срабатываний нового
tap/long-press) и назван способ доказательства (`browser smoke`, `unit`,
`mutation witness`, стандартные гейты) — ни один не сводится к «проверить, что
код скомпилировался».
- AC8 заранее требует «отрицательных свидетелей» (mutation witness, красный при
снятии каждой из двух защит по отдельности) — снимает типовой риск теста,
который не умеет падать, до того, как код написан.
- Технический анализ причины дефекта (два независимых пробела: bubble-phase
`_pointerDown` не проверяет multitouch; `contextmenu` не входит в
capture-guard) подтверждён построчным чтением кода на точном SHA, а не
принят на веру — обе претензии верны.
- Раздел 14 («принятые предположения») корректно выделяет ровно то, что
пользователь не наблюдает (внутреннее представление barrier, способ отличить
synthetic touch-`contextmenu` от настоящего), помечает это как «свободно
меняется» и не выдаёт технические догадки за продуктовые факты нигде за
пределами этого раздела — я не нашёл утверждений о поведении, которого нет ни
в `TOUCH-SUPPORT.md`, ни в `USER-GUIDE.ru.md`, ни в коде, без такой пометки.
- Терминология («долгое нажатие, 600 мс», «внутренняя карточка House Plan»,
«HA more-info», «правый клик») дословно совпадает с `docs/USER-GUIDE.ru.md`,
не изобретена заново.
- Вне-scope (раздел 4) корректно исключает изменение таймаутов, самих жестов
pan/pinch/double-tap, действий устройств, конфигурации, i18n и общей event
architecture — граница задачи не расползается за пределы двух названных
пробелов.
- Выбор полного трека вместо `small` обоснован явно и по существу (несколько
путей активации, изменение touch-контракта), а не декларативно.
- «Вопросы владельцу: нет» — оправдано: ожидаемое поведение уже зафиксировано в
действующем `docs/TOUCH-SUPPORT.md` («once a second touch joins... whole
sequence is navigation only»), новых продуктовых решений задача не требует;
открытых продуктовых вопросов (что видит/делает человек, объём изменений) я
тоже не нашёл — оба найденных Low технические, не продуктовые, и решаю их
сам, не выношу владельцу.
- Откат (revert коммита, без данных/миграции) и риски (§12) покрыты
перечисленными AC3–AC7 по существу, а не общей фразой.
## Чего не проверял
- Код ещё не написан — не проверялись типы гейтов (`typecheck`/`test`/`build`),
golden, smoke-запуски: это стадия code-review, не spec-review.
- Не проверял `scripts/mutation-registry.mjs` на предмет точного формата записи
двух будущих witness-мутаций — техническая деталь реализации, не предмет
ревью ТЗ.
- Не запускал `node scripts/smoke-select.mjs` — на этой стадии диффа кода нет,
инструмент неприменим.
## Вердикт
Зелёный. High — 0, Medium — 0. Обе Low-находки решены самим ревьюером с
записью (без правки ТЗ и без возврата автору), как предусмотрено PROCESS.md
§2.4 для Low.
**Готово к разработке** — но перевод статуса делает конвейер (эта роль его не
меняет вручную).
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `dev`, коммит `ad4000f95a15` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `752738456c32b4d4f015527719edba343c434395`
```
git log --all --format='%H %T' | grep 752738456c32
```
- Тело issue: `ec0f61cba0a9ebb0afa611f400145755e567714b91b307e18928aed57bbb61be`
- Вердикт конвейера: `green` · High 0