docs: review document for #318

Issue: #318
User-Visible: no
This commit is contained in:
claude[bot]
2026-08-28 13:07:58 +00:00
parent 5514fdfe15
commit 4b81dc2ad9
+188
View File
@@ -0,0 +1,188 @@
# 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.