# SPEC-REVIEW-251-r1 - Issue: [#251](https://github.com/Matysh/houseplan-card/issues/251) — «доступность контроллера не наследуется от управляемой цели» - Этап: ТЗ на ревью (PROCESS.md §2.4) - Заход: r1 · блокирующих циклов израсходовано 0 из 4 (первый раунд, разбор полный) - Артефакт ТЗ: `docs/specs/251-controller-target-availability.md` (355 строк), запись в `docs/specs/README.md:130` - Ветка: `issue/251-controller-target-availability` - Ревьюер получил issue и ТЗ без устных пояснений автора; ниже — независимая проверка по коду `dev`. ## Скоуп ревью Диапазон `origin/dev..HEAD` — один коммит `ba0c787` («docs(spec): separate controller and target availability»), трейлеры `Issue: #251` / `User-Visible: no` на месте, класс C (документация), правка сама по себе не требует `User-Visible: yes`. Изменены только `docs/specs/251-*.md` и `docs/specs/README.md`. Продуктовый код (`src/**`) не тронут — это ожидаемо для этапа ТЗ. Предмет ревью: полный текст ТЗ против §7.1 PROCESS.md (обязательные разделы), против диагноза в теле issue и трёх комментариев владельца/аналитика, и против реального кода `src/device-presentation.ts` / `src/device-toggle.ts` / `src/houseplan-card.ts` — верификация того, что заявленные в ТЗ факты о текущем поведении не догадка, а точное описание существующего кода. ## Как проверялось 1. Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` §2.4/§2.9/§7.1/§7.2, `docs/USER-GUIDE.ru.md` (разделы «Жесты маркера», «Действия», «Визуальные состояния устройств», §12), `docs/TOUCH-SUPPORT.md`. 2. Прочитано тело issue #251 и все 6 комментариев (диагноз владельца, актуализация аналитики, claim-комментарий, вопрос Q1, решение владельца, хендофф на ревью). 3. Гейты этапа spec: - `node scripts/check-docs.mjs --external` → `Documentation checks passed (7 files, 10 external links)` — green; - `node scripts/process-gate.mjs --range origin/dev..HEAD --issues` → `гейт пройден, предупреждений 0` — green. Оба совпадают с тем, что заявил автор в хендоффе. 4. Построчная сверка технических утверждений ТЗ с кодом: - `resolvePresentationSources()` в `src/device-presentation.ts:233-369` — подтверждено, что для маркера с `controls` `visualSources`/`samples` строятся исключительно из `lights` (controls-derived + owned light sources) и **никогда** не читают `d.entities` устройства-контроллера (battery/LQI/event туда не попадают в принципе, кроме ветки `alarm` в `criticalSources`). Это точное описание дефекта, не догадка. - `resolveDevicePresentation()` (там же, 580-711) — `visual = combineVisualSamples(sources.samples)`, класс `unavail` берётся из `visual.availability === 'unavailable'` (строка 564) раньше, чем `working`/`open` — подтверждает утверждение ТЗ §6.1 о приоритете `unavail` над `working`. - `src/device-toggle.ts`: `SkippedToggleTarget{ref, entityId, name, reason}` (37-58), `toggleOperation()` (763-767) возвращает `null`, когда нет `command`/`operation` — подтверждает опору AC3/AC4/AC5 на реальную, уже существующую классификацию, а не на новый API. - `resolveOwnEntity()`/`reasonForSingle()` (405-497) — для одиночной цели `noneReason` уже мапит `missing → 'unavailable'`, `skippedTargets` сохраняет причину и имя — ровно то, что нужно для локализованного сообщения без нового запроса к HA (заявление аналитика подтверждено). - `_clickDevice()` в `src/houseplan-card.ts:4930-4979` — строка 4932, `if (!initial || !toggleOperation(initial)) return; // ... quiet no-op` — это именно тот тихий `return`, который ТЗ называет дефектом второй части (п.3 «Актуализации аналитики»). Подтверждено чтением, не исполнением. - Токст toasta: `src/houseplan-card.ts:16473,17069` — `