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

189 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 — 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<N-1>
Неприменимо — это первый заход (r1), предыдущего раунда нет.
## Вердикт
Зелёный. Спецификация полна по §7.1, продуктовый вопрос закрыт владельцем до
написания, технический диагноз и контракт проверены построчным чтением кода
и совпадают с реальностью, скоуп/не-скоуп корректно ограничивают риск
ослабления контракта #251, AC доказуемы существующими или явно
запланированными механизмами. High: 0. Medium: 0.