Files
houseplan-card/docs/reviews/SPEC-REVIEW-29-r1.md
T
2026-08-27 23:17:52 +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-29-r1
- Issue: https://github.com/Matysh/houseplan-card/issues/29
- Этап: spec (PROCESS.md §2.4)
- Заход: r1 · блокирующих циклов израсходовано 0 из 4
- ТЗ: `docs/specs/029-device-inbox-lifecycle.md`
- SHA материала ревью: `a6ce1ae7e534fa4b41fcc0ab93e5ed95a693c4c1` (ветка `issue/29-device-inbox-lifecycle`)
- Трек: обычный (не `small`), лимит циклов ревью ТЗ — 4.
Это первый заход ревью по этой задаче — предыдущего вердикта в issue нет,
раздел «Унаследовано» не применяется, разбор полный.
## Скоуп разбора
Проверено:
1. `docs/SCOPE.md` — соответствие job'ам J4/J6, отсутствие выхода за
продуктовую рамку (out-of-scope список, standing rules).
2. `AGENTS.md` и `PROCESS.md` §2.4, §7.1 — формальные требования к ТЗ и
формат вердикта.
3. Тело issue #29 и все комментарии, включая повторную актуализацию анализа
от 2026-08-28 и решение владельца по Q1.
4. `docs/USER-GUIDE.ru.md` (терминология «Скрытые и деактивированные»,
«Редактор устройств», раздел про скрытие/удаление) и `docs/FILTERING.md`
(канонический контракт hidden/removed/tombstone/entity-ownership).
5. Исходный код: `src/houseplan-card.ts` (`_bindingCandidates`,
`new_device_ids`, `_maybeRebuildDevices`, `.slice(0, 200)` дважды),
`src/ha-binding-status.ts` (`HaBindingStatus.kind`), `src/types.ts`
(`marker.hidden`, `marker.removed`), `src/i18n/ru.json` и `en.json`.
6. Существующие тесты, упомянутые в AC11/§16: `test/devices.test.mjs`,
`test/ha-binding-status.test.mjs`, `test/device-presentation*.test.mjs`,
`demo/smoke_hidden_flag.mjs`, `demo/smoke_binding_picker.mjs` —
существуют, содержимое `smoke_hidden_flag.mjs` прочитано целиком.
7. `docs/CONFIG-COMPATIBILITY.md` — беглая проверка на отсутствие
противоречий заявлению «нет новых persisted-полей».
Ревью — на этапе ТЗ: продуктового кода нет, автотесты не запускались (их
ещё не существует), гейты §8 к этому этапу не относятся.
## Метод проверки
Каждое фактическое утверждение ТЗ о текущем поведении продукта
(«До», §3, §7, §8, §9, §10.1) сверено с кодом или каноническим документом, а
не принято на слово автора. Отдельно проверено, есть ли в тексте
утверждение о поведении, которого нет ни в одном документе и которое не
помечено как предположение (раздел 21 ТЗ).
## Находки
### M1 (Medium, в скоупе) — исчезновение призрачного показа скрытых/HA-disabled маркеров на плане не названо как продуктовое решение
**Файл:** `docs/specs/029-device-inbox-lifecycle.md`, §10.1 (строки 216–222).
Сегодня редактор устройств может показать скрытые и HA-disabled маркеры
прямо на плане, в их реальной позиции, призраками — режим переключается
кнопкой «Скрытые и деактивированные» (`docs/FILTERING.md` строки 84–91,
`docs/USER-GUIDE.ru.md:178` — «доступны скрытые маркеры», перетаскиваются;
подтверждено в `demo/smoke_hidden_flag.mjs`, где ghost рендерится в
`_setMode('devices')` с `_showHidden = true` и клик по нему открывает
диалог). Это единственный способ увидеть, ГДЕ на плане сидит скрытое или
деактивированное устройство, не отменяя его скрытость.
ТЗ прямо убирает этот режим: «Скрытые markers больше не рисуются поверх
плана постоянным локальным режимом: доступ к ним даёт каталог» (§10.1).
Но каталог (§10.2–10.4) не даёт эквивалента: `Find` явно доступен «только
если marker реально отрисовывается» (§10.4), а для строк категории
«Скрытые» и для «На плане, временно не отображается из-за HA status»
primary/secondary действия — это «Показать»/«Настроить», не позиция на
плане. Значит, чтобы увидеть, где стоит скрытый маркер, администратору
придётся сначала его показать (что меняет конфиг), посмотреть, и при
необходимости скрыть обратно — вместо непосредственного просмотра.
Это видимое пользователю изменение объёма функциональности («какая
персона что видит и делает» — ровно тот класс вопросов, который согласно
PROCESS.md §7.1 задаётся владельцу или явно фиксируется как принятое
предположение в §21). В тексте ТЗ оно подано как самоочевидный
технический побочный эффект объединения кнопок, а не как решение,
которое можно оспорить: обоснование «устраняет состояние панели,
неочевидное после возврата в редактор» — это плюс нового дизайна, но оно
не адресует потерю прямого просмотра позиции.
**Не High**, потому что: обходной путь существует (Показать → посмотреть/
перетащить → Скрыть), это не потеря данных и не поломка AC — просто
непроверенное продуктовое допущение, которое дёшево закрыть на этом этапе.
**Как закрыть в этом же цикле (на выбор автора):** либо явно вынести это в
блок §21 как предположение, которое ревьюер/владелец может оспорить, с
описанием обходного пути; либо задать это владельцу одним пакетным
вопросом с предложенным дефолтом (например: «Find для скрытой/disabled
строки временно подсвечивает позицию на плане не снимая hidden» как
альтернативный дизайн); либо сознательно сохранить упрощённый вариант, но
явно назвать компромисс и обходной путь в §2 «До/После» и в §18 «Риски».
### M2 (Medium, в скоупе) — устаревающая строка i18n не включена в план обновления
**Файлы:** `src/i18n/ru.json:673`, `src/i18n/en.json:673` (`marker.hide_tip`).
Текущий текст подсказки при скрытии маркера дословно ссылается на кнопку,
которую это ТЗ удаляет: RU — «Вернуть его можно через кнопку «Скрытые и
деактивированные» в редакторе устройств»; EN — `Restore it through "Hidden
and disabled" in the device editor`. §10.1 заменяет обе кнопки («Добавить»
и «Скрытые и деактивированные») одной кнопкой «Устройства» (`devbar.add`,
`devbar.show_all` перестают существовать в UI в текущем виде).
Раздел 14 (i18n) перечисляет только **новые** ключи и не содержит пункта
«обновить существующие ключи, ссылающиеся на удаляемые элементы
интерфейса». Если реализовать ТЗ как написано, `marker.hide_tip` останется
нетронутым и после релиза будет указывать пользователю нажать
несуществующую кнопку — конкретный, проверяемый дефект, а не гипотетический.
**Как закрыть:** добавить в §14 явный пункт «`marker.hide_tip` (en+ru)
обновляется, чтобы указывать на новую точку входа «Устройства»» (и
проверить, нет ли других строк с той же ссылкой — быстрый `grep` по
`show_all`/«Скрытые и деактивированные» в `src/i18n/*.json` показывает,
что это единственная пара строк такого рода, кроме самих `devbar.*`
/`title.show_all`, которые и так меняются по §10.1).
Обе находки Medium, в скоупе задачи (правки в самом файле ТЗ) — по
PROCESS.md §2.4/§2.7 они не создают отдельный issue (решение владельца
2026-08-19, #202) и возвращают ТЗ автору с жёлтым вердиктом.
## Что проверено и признано корректным
- **Формальные разделы §7.1** — сценарий, «что человек увидит до/после»,
проблема, скоуп/не-скоуп, контракт поведения, UX, модель данных и
миграция, i18n, AC1…AC11 с указанием способа доказательства, план
автотестов, риски, откат, release-артефакты — все присутствуют.
- **AC1–AC11** однозначны и у каждого назван способ доказательства
(unit/smoke/golden/review); ни один не описывает недоказуемое поведение.
- **Q1 владельца** (классификация автообнаруженного видимого устройства
без marker) корректно отражена в §7.2 (категория `on_plan`) и в AC3 —
соответствует решению владельца в комментарии issue.
- **Заявления о текущем поведении, использованные как база для «До»,
подтверждены кодом и канонoм**, а не додуманы:
- `_bindingCandidates` в `src/houseplan-card.ts:14032` — существующий
eligibility-код, который ТЗ предлагает извлечь в общий helper (§12);
не выдумка.
- `HaBindingStatus.kind` (`active`/`ha_disabled`/`orphaned`/`unverified`)
в `src/ha-binding-status.ts:14-17` — точное совпадение с таблицей §7.3.
- `marker.hidden`/`marker.removed` в `src/types.ts:118,121` — существуют,
семантика совпадает с `docs/FILTERING.md`.
- Жёсткий кап на 200 элементов в существующих списках кандидатов
(`src/houseplan-card.ts:14106`, `19114` — `.slice(0, 200)`) —
подтверждает описанную в §8.1/AC9 проблему, которую каталог обязан не
унаследовать.
- `duplicate_name_area` действительно устарела: `docs/FILTERING.md:145`
прямо говорит «Duplicate names are still numbered», не скрываются.
- `settings.new_device_ids` фильтруется от уже скрытых id при seed
(`src/houseplan-card.ts:3679–3684`) — подтверждает §8.2 «Первично
отфильтрованный скрытый кандидат не получает badge, как и сегодня».
- Терминология «Добавить», «Скрытые и деактивированные», «Правила
иконок» — точное совпадение с `src/i18n/ru.json:455-457` и
`docs/USER-GUIDE.ru.md`.
- **Технические предположения (§21)** промаркированы явно и корректно
отделены от продуктовых решений — кроме пробела, описанного в M1.
- **Не входит (§6)** корректно исключает #44 (discovery-настройки), #126
(смена area), #109 (multi-channel), bulk-операции, историю/графики,
touch-паритет — всё по `docs/SCOPE.md` и `docs/TOUCH-SUPPORT.md`, без
расширения скоупа.
- **Откат (§19)** реалистичен: нет новых persisted-полей и версии модели,
откат — вернуть старые кнопки; совместимость с созданными через «Скрыть
из списка» обычными `hidden:true` маркерами сохраняется.
- Названные тестовые артефакты существуют: `test/devices.test.mjs`,
`test/ha-binding-status.test.mjs`, `test/device-presentation.test.mjs`,
`test/device-presentation-policy.test.mjs`, `demo/smoke_hidden_flag.mjs`,
`demo/smoke_binding_picker.mjs`, `scripts/smoke-select.mjs` — ни один не
является выдумкой.
## Чего не проверял
- Не запускал никакие гейты (`typecheck`/`test`/`build`/смоки) — на этапе
ТЗ продуктового кода нет, это не применимо (§8 относится к
код-ревью).
- Не проверял `docs/ARCHITECTURE.md` и `docs/CANVAS.md` целиком постранично
— только точечно то, что касается заявленных в ТЗ архитектурных решений
(pure resolver, eligibility helper); полный аудит этих документов не
требуется для ревью ТЗ.
- Не оценивал производительность реализации (её ещё нет); проверил только,
что заявленная асимптотика (§12, §17) не противоречит существующему коду
(`.slice(0, 200)` факт капа подтверждён).
- Не проверял golden/скриншот-инфраструктуру предметно — она релевантна
коду, а не ТЗ.
## Вердикт
Полностью выполненные формальные требования ТЗ (§7.1) не отменяют жёлтый
вердикт: две находки Medium в скоупе (M1, M2) — реальные пробелы, оставить
их «в тексте ревью» без правки запрещено §12 PROCESS.md. High-находок нет.
**Вердикт: жёлтый.** ТЗ возвращается автору на правку M1 и M2; после
правки — новый заход ревью по дельте (PROCESS.md §2.10).