Files
houseplan-card/docs/reviews/SPEC-REVIEW-267-r1.md
T
2026-08-27 18:26:00 +00:00

18 KiB
Raw Blame History

SPEC-REVIEW-267-r1

  • Issue: https://github.com/Matysh/houseplan-card/issues/267
  • Этап: ТЗ на ревью (PROCESS.md §2.4)
  • Заход: r1 · блокирующих циклов израсходовано 0 из 4 (первый заход)
  • Материал: docs/specs/267-device-presentation-decision-table.md на SHA c5e14526 (ветка issue/267-device-presentation-table), плюс тело issue и два комментария («Аналитика», «ТЗ готово к независимому ревью»)
  • Вердикт: зелёный

Скоуп ревью

Первый заход — разбор полный. Проверялось:

  1. соответствие docs/SCOPE.md (какой Core user job закрывает задача);
  2. обязательные разделы ТЗ по PROCESS.md §7.1 и однозначность/доказуемость каждого AC;
  3. отсутствие догадок, выданных за факт — каждое «зафиксированное продуктовое решение» (§3 ТЗ) сверено с первоисточником (docs/USER-GUIDE.ru.md §12, issue #251/#274/#98) построчно;
  4. измеренная база (строки/ветвления/имена функций) сверена с текущим src/device-presentation.ts на HEAD;
  5. технические допущения (§20 ТЗ) — что помечено как «можно менять на ревью», не выдано за продуктовое решение;
  6. инструменты, на которые ссылается план тестов (scripts/mutation-gate.mjs --check/--id=, scripts/smoke-select.mjs, существующие smoke-файлы), существуют и поддерживают заявленный интерфейс.

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

Что Команда/действие Результат
Класс изменения коммита git show --stat HEAD только docs/specs/267-*.md + docs/specs/README.md — класс C, соответствует заявленному в шапке ТЗ
Измеренная база §2 ТЗ wc -l src/device-presentation.ts, grep -n на resolvePresentationSources/resolveDevicePresentation 781 строка, функции на строках 252→404 (152) и 600→781 (181) — совпадает с заявленными 781/152/182
Существование смежных модулей §7.2 ls src/device-*.ts device-visual.ts, device-pulse.ts, device-value-badge.ts, device-face.ts, device-toggle.ts — все существуют, как заявлено
Соответствие §3.2 (#251) чтение docs/USER-GUIDE.ru.md:914-921 текст руководства слово в слово подтверждает решение (доступность контроллера только по своим сущностям, виртуальный контроллер доступен)
Соответствие §3.3 (#274) чтение docs/USER-GUIDE.ru.md:922-924 подтверждено: удалённая цель не делает контроллер недоступным и не расходит план/preview
Соответствие §3.4 (#98, пульсации) чтение docs/USER-GUIDE.ru.md:945-963 подтверждено: обычная пульсация только в «Значок + состояние и активность», статичных колец нет
Соответствие §3.5 (static_icon) чтение docs/USER-GUIDE.ru.md:1007-1014 подтверждено дословно, включая исключение hover/controls/Glow/заливки
L05 (hidden_design_preview) не выдумка grep -rn hidden_design_preview src/ test/ строка уже существует в device-presentation.ts:87,728, i18n/ru.json:236, покрыта test/device-presentation.test.mjs:751 — существующее поведение, не новая догадка
F09–F12 (value fallback reasons) grep -rn value_ambiguous_sources|value_virtual|activity_display_disabled все три reason уже в коде и тестах
F16 (LQI-полосы 0/40, 41/179, 180+) чтение markerLqiBand() (device-presentation.ts:46-49) точное совпадение границ
Инструмент mutation-gate.mjs --id=/--check grep -n "'--check'" , grep -n "startsWith('--id=" оба флага существуют и поддерживаются
Смоки, названные в ТЗ ls demo/smoke_*.mjs | grep -E "wireless|device_icon" smoke_wireless_controller_parity.mjs, smoke_device_icon_design.mjs существуют
Легаси-совместимость ripple→icon_ripple (§13) чтение normalizeDeviceDisplay() (src/logic.ts:852-856) подтверждено дословно
Связанные issues реальны и закрыты gh issue view 98/251/274/34 #98, #251, #274 закрыты и по теме совпадают с описанием в ТЗ; #34 открыт как архитектурный umbrella — корректная ссылка, не блокирующая зависимость
Уникальность decision row ID grep по ^| L0|^| S0… + uniq -d дубликатов ID нет (30 строк: 6 L, 13 S, 17 F, 8 A)
Порядок приоритета §7.3 vs реальный код чтение resolveDevicePresentation() целиком (device-presentation.ts:600-781) заявленный порядок (lifecycle → static → alarm переживает live-gate → live-gate → controller availability → …) соответствует фактической последовательности веток в текущей реализации — не выдумана «идеальная» модель без связи с кодом
Индекс docs/specs/README.md git diff HEAD~1 HEAD -- docs/specs/README.md строка #267 добавлена в том же коммите, ссылка верна
node scripts/check-docs.mjs прогнан Documentation checks passed (7 files, 10 external links) — зелёный
git diff --check на коммит спеки git diff --check HEAD~1 HEAD -- docs/specs/267-*.md не зелёный: 548: new blank line at EOF (см. находки, Low)

Гейты typecheck/test/build не гонялись: коммит c5e14526 — чистый класс C (только docs/specs/**), продуктовый код не менялся, src/** не тронут, и check-docs.mjs (который зависит от src/**) уже прогнан и зелёный. Браузерные смоки, golden, инварианты геометрии, backend-тесты — неприменимы: diff не касается src/**/custom_components/** в этом заходе, это ещё не реализация.

Находки

Low-1 — заявление «git diff --check — green» в хендоффе не совпадает с фактом

Файл: issue #267, комментарий «ТЗ готово к независимому ревью» (2026-08-27T18:17:33Z).

Автор указал git diff --check как один из двух прогнанных чеков и заявил результат «green». Повторный прогон git diff --check HEAD~1 HEAD -- docs/specs/267-device-presentation-decision-table.md даёт 548: new blank line at EOF (файл заканчивается двумя переводами строки вместо одного), exit code 2. Само по себе это не искажает содержание ТЗ и не влияет ни на один AC — check-docs.mjs, единственный настоящий гейт для доков в этом диффе, зелёный. Но правило «"Verified" без названного результата не является доказательством» работает в обе стороны: названный результат должен быть точным. Ложноположительный «green» в хендоффе — ровно тот тип записи, который процесс просит не допускать.

Решение ревьюера: снимается без правки. Косметическая лишняя пустая строка в конце файла, не блокирует переход в S5-ready; будет естественно убрана следующим коммитом по ветке (код или доп. правка спеки), отдельного возврата на цикл не оправдывает.

Что не является находкой (проверено и отклонено как ложное подозрение)

  • Отсутствие отдельных заголовков «UX» и «Модель данных и миграция». PROCESS.md §7.1 требует эти темы по содержанию, а не буквальным заголовком. Содержание есть: §1 «Что человек увидит» прямо формулирует отсутствие видимых изменений, §12 не-скоуп явно исключает новые статусы/цвета/иконки/ display-режимы, §13 явно закрывает данные/миграцию («отсутствуют», normalizeDeviceDisplay() остаётся read-gate). Прецедент того же жанра задач — docs/specs/264-resize-controller.md — построен по не идентичному, но сопоставимому по духу набору разделов. Не находка.
  • «Открытых продуктовых вопросов нет» при сложности 7/10. Проверено предметно, а не на слово: все три продуктовых вопроса из тела issue («что видит человек при доступном контроллере/недоступной цели», «приоритет осей при конфликте», «сколько состояний имеет право быть различимыми») закрыты ссылкой на уже принятые решения (#251, #274, USER-GUIDE §12) и на явный не-скоуп «новых статусов/цветов/иконок нет» — это не отказ отвечать, а предметный ответ «поведение не меняется, только структура кода». Для refactor-only задачи с зафиксированным контрактом это корректно, а не недосмотр.
  • Достижимость AC2 («renderer и surfaces не принимают альтернативных решений по raw HA state»). В houseplan-card.ts действительно остаются прямые обращения к hass.states[...] (vacuum-телеметрия, friendly_name для подписей/тултипов, состояние contact/lock для соседних, не face-related элементов). AC2 ограничен явно: «Lifecycle/display/status/content/ diagnostics gates из resolveDevicePresentation()» — то есть про вынос уже существующих внутри функции веток, а не про полный запрет чтения hass.states во всём файле. Формулировка не создаёт невыполнимое требование.

Проверка обязательных разделов (PROCESS.md §7.1)

Сценарий и персона (§1) — есть, персона из docs/SCOPE.md (домашний администратор + разработчик как второй адресат документа). Что человек увидит до/после (§1) — есть, явно «ничего нового». Проблема (§2) — есть, с измеренной и подтверждённой базой. Скоуп/не-скоуп (§11/§12) — есть, не-скоуп явно закрывает риск декартова произведения и попутных продуктовых изменений. Контракт поведения — распределён по §3 (зафиксированные решения) и §6 (таблицы рядов), содержательно присутствует. UX/данные и миграция — покрыты содержательно (см. «что не является находкой» выше). i18n — явно «нет новых строк» (§13), проверено: задача не должна трогать src/i18n/*.json, её и не трогает вне ТЗ. AC1…AC10 — пронумерованы, у каждого указан способ доказательства (§15). План автотестов — есть (§16), с точным списком команд перед S7. Риски — есть (§18), адресуют именно те риски, которые релевантны этому рефакторингу (декартово произведение, второй resolver, plan/preview расхождение, регрессия pixel-parity). Откат — есть (§19), корректен для чисто структурного рефакторинга без миграции. Release-артефакты — есть (§17), корректно User-Visible: no.

Проверка AC на однозначность и доказуемость

Каждый из AC1–AC10 формулирует наблюдаемое или механически проверяемое условие и называет способ доказательства (unit/architecture test/mutation registry/smoke/golden/code review), без AC вида «работает корректно» без критерия. AC4 и AC5 образуют закрытую петлю «документ ↔ fixture ↔ mutant», которая явно защищает от того самого дрейфа, ради которого затевается задача (документ описывает желаемое, а не то, что тест умеет проверить). AC8 — единственный жёсткий pixel/config-контракт рефакторинга — корректно замкнут на существующий golden:verify и docs screenshot gate, а не на новое самодельное сравнение.

Отдельно проверено на воспроизводимость до кода: порядок приоритетов §7.3 не является постулированной автором целевой моделью, оторванной от реальности — он совпадает с фактической последовательностью условий в resolveDevicePresentation() на HEAD (эффективная скрытость → static → alarm переживает live-gate → live-gate → controller availability → value/icon/pulse). Это значит рефакторинг, скорее всего, реализуем без скрытого изменения поведения, а не переписывает контракт под видом документирования.

Унаследовано / предыдущие раунды

Неприменимо — это первый заход (r1) по этому этапу.

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

  • Реализацию (класс A/B) — её ещё нет, диапазон S3-spec/S4-spec-review правок кода не касается.
  • typecheck/npm test/npm run build/браузерные смоки/golden/инварианты геометрии/backend — неприменимы к чисто документационному коммиту этого захода; будут обязательны на код-ревью.
  • Реальную достижимость AC9 (O(1) policy) и AC7 (surface parity) на уровне производительности/поведения — это утверждения о будущей реализации, доказываются на код-ревью, а не на этапе ТЗ.
  • Полноту декомпозиции §6 (все ли нужные строки перечислены, не пропущена ли комбинация) — не пересчитывал декартово произведение осей вручную; проверил выборочно, что перечисленные строки соответствуют реальным веткам кода и реальным issue-регрессиям (#251, #274), и что ID уникальны. Полнота набора рядов — предметная (product) оценка автора и владельца, а не то, что ревьюер ТЗ обязан пересчитать с нуля при отсутствии признаков пропуска.