Files
houseplan-card/docs/reviews/SPEC-REVIEW-318-r1.md
T
2026-08-28 13:07:58 +00:00

16 KiB
Raw Blame History

SPEC-REVIEW — issue #318 · заход r1

  • Этап: spec (PROCESS.md §2.4)
  • Артефакт ТЗ: docs/specs/318-empty-controller-roster.md (полный трек; docs/specs/README.md — строка добавлена)
  • SHA ревью: 1242a3b1badf13a8785c7e176da2c3f765ccdad8 (ветка issue/318-switch-render-state, коммит "docs: specify empty-roster controller state", Issue: #318 · User-Visible: no — трейлеры корректны: коммит документальный, пользовательского поведения ещё не меняет)
  • Заход: r1 · блокирующих циклов израсходовано 0 из 4

Скоуп

Issue #318: маркер физического беспроводного выключателя без единой собственной сущности registry (device: binding активен, own roster = [], controls: [light.wall_lights]) навсегда остаётся в приглушённой (unavailable) подаче независимо от состояния управляемой цели. Owner уже принял продуктовое решение в комментариях issue до написания ТЗ: активный физический device-binding с пустым собственным roster считается доступным и следует controls; непустой roster без единого живого состояния остаётся unavailable по контракту #251. ТЗ реализует это решение.

Трек — полный, обоснованно: меняется публичный UX-контракт (строка S05 таблицы решений docs/DEVICE-PRESENTATION.md), затронут общий presentation resolver на всех поверхностях (plan/preview/hosted Static). Названный критерий лёгкого трека, который задача не проходит, указан в самом ТЗ.

Затронутая подсистема: docs/DEVICE-PRESENTATION.md / docs/ARCHITECTURE.md (канон availability/status controller), плюс docs/USER-GUIDE.ru.md для пользовательской терминологии.

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

  1. Прочитан docs/SCOPE.md — задача закрывает J1 («что происходит сейчас») и J3 («быстрое очевидное действие»); ложный сигнал недоступности прямо противоречит J1.
  2. Прочитаны AGENTS.md, PROCESS.md §1–2.4, §4, §5, §7.1–7.3 — формат документа, обязательные разделы, бюджет циклов, критерий полного трека.
  3. Прочитано тело issue #318 и все 4 комментария: заявка → анализ аналитика с продуктовым вопросом → решение владельца → хендофф автора ТЗ. Продуктовый вопрос был ровно один, и он закрыт до старта этого ревью.
  4. Прочитан весь файл ТЗ docs/specs/318-empty-controller-roster.md (295 строк) целиком, раздел за разделом на соответствие §7.1.
  5. Прочитаны канонические документы: docs/DEVICE-PRESENTATION.md (таблица решений, строки S03–S09), docs/ARCHITECTURE.md (цитаты «Yellow means working» из issue подтверждены как существующий канон), docs/USER-GUIDE.ru.md (раздел 12 «Визуальные состояния устройств», строки 958–1004 — текущая документированная формулировка, которую ТЗ обязано будет уточнить в коде).
  6. Сверены с реальным кодом все технические утверждения диагноза (§3) и контракта (§6.1–6.3), а не приняты на веру:
    • controllerAvailability() (src/device-presentation.ts:208-217) — (d.entities || []).some(...) на пустом массиве безусловно даёт false → unavailable. Диагноз ТЗ («пустой список безусловно даёт unavailable») подтверждён чтением, не исполнением.
    • resolveHaBindingStatus() (src/ha-binding-status.ts:414-463) — ветка kind === 'device', allEntityIds.length === 0: isRegistryEntryEnabled(device) истинно (устройство активно), enabledEntityIds = [].filter(...) = [], условие allEntityIds.length && !enabledEntityIds.length ложно (первый операнд 0) → возвращается { kind: 'active', enabledEntityIds: [], allEntityIds: [] }. Это ровно сценарий #318 и ровно предикат §6.1 ТЗ («active», bindingKind === 'device', roster []). Подтверждено чтением.
    • Термины bindingKind, bindingStatus.kind (active/ha_disabled/ orphaned/unverified), DevItem.entities — все существуют в src/devices.ts, src/ha-binding-status.ts с ровно теми значениями, которые ТЗ им приписывает.
    • Идентификаторы тестовых артефактов из плана и AC — все существующие, не выдуманные: test/device-presentation.test.mjs (1092 строки), demo/smoke_wireless_controller_parity.mjs (162 строки), scripts/mutation-gate.mjs содержит мутанты controller-availability-follows-target, wireless-controller-loses-filtered-target-role, wireless-controller-preview-drops-sibling-markers (используются также в test/fixtures/device-presentation-decisions.mjs для S03/S05/S07).
    • docs/DEVICE-PRESENTATION.md строка S05 сегодня буквально гласит «цель работает, controller не имеет live entity → faded controller» — ТЗ §9 корректно называет её на разделение.
    • docs/USER-GUIDE.ru.md:979 и таблица строка 1000 сегодня документируют «отсутствуют» (нулевой roster) как один из триггеров бледного маркера — это ровно текст, который реализация обязана сузить; ТЗ §9 называет этот документ в списке правок, не давая финальной формулировки (корректно — формулировка не продуктовый факт, а редактура канона).
  7. Проверено соответствие обязательного чек-листа docs/specs/README.md («Обязательные release-артефакты номерного ТЗ») — все четыре пункта закрыты разделами §9 и §15 ТЗ.
  8. Проверено, что коммит на ветке ровно один, docs-only, класс C по AGENTS.md (не требует ревью само по себе, но здесь — часть DoD этапа spec), трейлеры корректны для docs-only коммита.

Гейты не гонялись: этап — ревью ТЗ, продуктового кода в диффе нет (класс C, только docs/**). Раздел «чего не проверял» — ниже.

Находки

Нет. Ни одной High- или Medium-находки.

Проверены типичные классы дефектов спек-ревью и не обнаружены:

  • Догадка, выданная за факт. Не найдена: единственный продуктовый вопрос (эмпти-roster active device = доступен?) задан аналитиком и решён владельцем до старта написания ТЗ (комментарии от 12:37 и 12:42). §16 ТЗ содержит явный блок «принято предположительно, поменять свободно» ровно для технических, не продуктовых решений (расположение предиката, отсутствие нового enum, ID decision trace) — как и требует §7.1.
  • Отсутствие открытого вопроса при сложной задаче. Вопрос был; он снят корректно, до, а не во время написания ТЗ.
  • Несуществующие идентификаторы/файлы в AC и плане. Все имена функций, тестовых файлов и mutation ID существуют в дереве репозитория (см. выше).
  • Расхождение с каноном подсистемы. Диагноз и предлагаемое правило не противоречат ни одному существующему решению таблицы S01–S09; #251 и #274 явно сохранены как негативные тесты (AC3, AC4).
  • Нарушение SCOPE.md. Изменение — узкое исправление существующего J1/J3 сценария, не расширяет функциональность и не вводит новый визуальный элемент (§5 «Не входит» явно запрещает новый badge/glyph/pulse/цвет/текст).
  • Единое число, два источника. Неприменимо: изменение касается только availability/цвета подложки, не вводит и не дублирует числовое значение.
  • Отсутствие раздела §7.1. Все обязательные разделы присутствуют: сценарий и персона (§1), что человек увидит до/после (§2), проблема (§3), скоуп/не-скоуп (§5), контракт поведения (§6), UX (§7), модель данных/миграция (§8), i18n (§9), AC1–AC9 с доказательствами (§10), план автотестов (§11), перф/безопасность (§12), риски (§13), откат (§14), release-артефакты (§15).
  • Владельцу вынесен технический вопрос под видом продуктового. Не найдено: единственный заданный вопрос («считать ли активный device-binding без сущностей доступным») — ровно продуктовый по определению §7.1 (что человек видит).

Что проверено и корректно

  • Формат документа, нумерация разделов, наличие всех обязательных пунктов §7.1 PROCESS.md.
  • Трек задачи (полный) обоснован названным критерием, а не молчаливым умолчанием.
  • Диагноз причины (§3 ТЗ) — подтверждён построчным чтением controllerAvailability() и resolveHaBindingStatus(), соответствует фактическому коду один в один.
  • Предикат «active physical device binding» (§6.1) — реализуем на существующих полях (bindingKind, bindingStatus.kind, d.virtual), не требует новых данных.
  • Матрица §6.2 непротиворечива и покрывает все девять комбинаций binding×roster×target из отдельного анализа, включая явно исключённые virtual/ha-disabled/orphaned строки.
  • Скоуп/не-скоуп (§5) точно очерчивает границу: entity-bound markers, registry-как-online-для-непустого-roster, новый visual vocabulary — всё явно исключено, что закрывает главный риск (широкое ослабление контракта #251).
  • AC1–AC9 каждый называет способ доказательства через существующий или явно спланированный тестовый механизм (unit, production-bundle smoke, mutation-gate, diff audit), ни один не оставлен голословным.
  • Release-артефакты (§15) и обязательный чек-лист docs/specs/README.md закрыты: changelog RU/EN, список затронутой пользовательской документации, явное заявление об отсутствии новых screenshot/golden baseline при условии зелёных существующих сьютов.
  • Откат (§14) описан и не требует обратной миграции данных, так как модель/ конфигурация не меняются (§8 подтверждён отдельно).
  • Ветка, коммит и трейлеры соответствуют AGENTS.md: issue/318-switch-render-state, один docs-only коммит, Issue: #318 + User-Visible: no, SHA хендоффа (1242a3b) совпадает с фактическим HEAD.

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

  • Гейты typecheck/test/build/bundle:sync/bundle:budget/check-docs — не гонялись. На этом этапе в диффе нет ни одного файла класса A/B/D (только docs/specs/**), продуктовый код не менялся, гонять их бессмысленно: они проверяют код, которого ещё нет. Ссылка на зелёный Validate 1242a3b1 (https://github.com/Matysh/houseplan-card/actions/runs/33172444930) относится к этому же SHA и подтверждает, что репозиторий в целом зелёный, но это не подменяет проверку самого ТЗ — она сделана вручную чтением.
  • Смоки, golden, mutation-gate, invariants — не прогонялись и не должны: это план на этап реализации (§11 ТЗ), сейчас проверяется только существование названных файлов/идентификаторов, не их поведение под ещё не написанный код.
  • Финальная формулировка правок канона (DEVICE-PRESENTATION.md S05, ARCHITECTURE.md, USER-GUIDE.md/USER-GUIDE.ru.md, TESTING.md) — ТЗ правомерно откладывает точный текст на этап реализации; проверено только, что список затронутых документов полон и совпадает с реальными текущими формулировками, которые придётся менять (см. «Как проверялось», пункт 6).
  • UI/браузер — на этапе ревью ТЗ ручного тестирования нет по определению этапа; кода для проверки не существует.

Унаследовано из r

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

Вердикт

Зелёный. Спецификация полна по §7.1, продуктовый вопрос закрыт владельцем до написания, технический диагноз и контракт проверены построчным чтением кода и совпадают с реальностью, скоуп/не-скоуп корректно ограничивают риск ослабления контракта #251, AC доказуемы существующими или явно запланированными механизмами. High: 0. Medium: 0.