Files
houseplan-card/docs/reviews/SPEC-REVIEW-485-r4.md
T
2026-09-09 03:50:50 +03:00

261 lines
24 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-485-r4
Issue: [#485](https://github.com/Matysh/houseplan-card/issues/485).
Материал: ветка `issue/485-radar-presence`, SHA `2f2e3fcaca98055b93e647fe8e11df0f1567f643`
(baseline r3 материал `21950437b4343deead48c955d06191ea15bb567a`, докс-only diff,
проверено `git diff --stat` — только `docs/specs/485-radar-presence-stage1.md` и
`docs/specs/485-radar-presence.md`; коммит `docs/reviews/SPEC-REVIEW-485-r3.md` (`749970bb`)
в дельту не входит, по прецеденту r2/r3).
Этап: **spec** (S4-spec-review). Заход: r4. Блокирующих циклов израсходовано 2 из 4
(r1 жёлтый — цикл 1/4; r2 зелёный — цикла не образовал, #227; r3 жёлтый — цикл 2/4).
Трек: полный (`gh issue view 485` — метки `P2, feature, S4-spec-review`, `small`
отсутствует). Ревьюер ≠ автор.
## Скоуп разбора (по дельте, PROCESS §2.9/§2.10, issue #214)
Единственная блокирующая находка r3 — **M-1**: новый гейт видимости раздела
(«структурные метаданные адаптера/реестра») тихо исключал ранее принятый адаптер
«любой числовой сенсор» (класс `range_v1`), не оставляя описанного ручного пути его
настроить. Найдено два варианта исправления: (1) явный ручной путь-эскейп для
неопознанного устройства, либо (2) явное сужение AC/таблицы профилей с отдельным
вопросом владельцу. Между r3 и r4 владелец подтвердил **Q5-default**
(issuecomment-5585013213, 2026-09-08T12:18:52Z): вариант (1) — вторичное действие
«Это радар присутствия» в дополнительных действиях редактора устройства.
`git diff 21950437..2f2e3fca --stat` (исключая коммит документа r3):
```
docs/specs/485-radar-presence-stage1.md | 101 +++++++++++++++++++++++++-------
docs/specs/485-radar-presence.md | 19 ++++--
```
Дельта локальна по объёму и по содержанию: это реализация уже принятого владельцем
Q5-решения, которое r3 уже разобрал как «новый поведенческий контракт» и проверил
широко (точки входа этапов 2/3, `docs/SCOPE.md`, прецедент условного раздела
пылесоса, `docs/UX-MODES.md`). r4 не открывает новую подсистему и не меняет контракт
повторно — он проверяет, что текст фикса **точно** реализует уже одобренный Q5,
не ослабляет ACL/безопасность и не вносит новой недекларированной догадки. Поэтому
разбор в этом раунде ограничен: (а) построчная сверка обеих правок с формулировкой
Q5-default и с «как чинится» M-1 из r3, (б) чтение всего файла на предмет
рассинхронизации формулировок (старый текст, оставшийся противоречить новому),
(в) проверка последствий для ACL/WS-контракта (единственное реально новое
техническое расширение — доступ к draft-источникам без общего HA-устройства).
Остальное (продуктовая рамка, границы этапов, модель идентичности, AC-таблицы
C/S2/S3 и S1 кроме S1-19/20, совместимость, touch-декларации, i18n кроме двух
новых ключей, перф-бюджеты) дельтой не задето — см. «Унаследовано из r3».
## Как проверялось
- `gh issue view 485 --json labels,state` — `OPEN`, `P2, feature, S4-spec-review`,
`small` отсутствует: трек не изменился.
- `gh api repos/Matysh/houseplan-card/issues/485/comments` — прочитан весь текущий
список комментариев issue от последнего разобранного (после r3) до конца;
подтверждено, что после хендоффа автора (`IC_kwDOTOcLQM8AAAABTOT_XA`,
2026-09-08T12:21:18Z) новых комментариев нет — это материал раунда.
- Найден и прочитан дословно комментарий владельца Q5-default
(`gh api .../comments -q 'select(.id==5585013213)'`) — сверен построчно с текстом
`Q5 default` в `485-radar-presence.md:54` и с разделом «Manual entry (Q5)» в
`stage1.md:93-118`: формулировка «вторичное действие в дополнительных действиях»,
«основной раздел скрыт до выбора», «отмена ничего не сохраняет», «общий
переключатель не меняется», «путь исправления сохранённой настройки остаётся» —
совпадают дословно по смыслу, расширений сверх решения владельца не найдено.
- Построчный `git diff 21950437..2f2e3fca` по обоим файлам (полный текст правки).
- `grep -n -i "manual" docs/specs/485-radar-presence-stage1.md` и по общему
контракту — проверено отсутствие рассинхронизации: ни один старый фрагмент
(«completely unidentifiable hardware… cannot be automatically distinguished»,
«unresolved metadata is not positive evidence», исполнение AC S1-19) не
противоречит новому тексту после правки; все ссылки на «ручной путь» согласованно
указывают на один и тот же механизм Q5.
- Проверено, что `docs/specs/485-radar-presence-stage2.md:75` и
`-stage3.md:86` (точки входа «Device editor → Presence on plan → …», не тронуты
дельтой) остаются валидны: обе продолжают описывать вход из уже
определённого/настроенного радара — ручное объявление лишь добавляет способ,
которым радар становится «настроенным» (`marker.radar` сохранён), не открывает
новый вход в этапы 2–3 в обход существующей модели.
- Проверено расширение WS/ACL-раздела (`stage1.md:495-501`, «Draft sources use the
same strict schema/size caps…»): новый текст явно требует, чтобы (а) существующий
маркер принадлежал запрошенному пространству и был `may_write`, (б) каждый явно
выбранный источник был независимо читаем текущим пользователем, (в) само ручное
объявление/фронтенд-eligibility не были авторизацией — сервер валидирует граф
источников самостоятельно. Это закрывает очевидный риск новой фичи (клиент
заявляет «это радар» и получает доступ к чужим данным): проверка per-source ACL
и `may_write` остаётся обязательной независимо от объявления.
- Проверено i18n: два новых ключа (`additional_actions`, `declare_radar`) добавлены
с обеими колонками en/ru в единственной таблице §8 stage1; остальные ключи не
тронуты дельтой.
- Проверено, что «где именно живёт эта кнопка» (свёрнутая группа «Additional
actions», отсутствие отдельного персистентного флага классификации) явно помечено
как реализационный выбор в блоке допущений в конце `stage1.md` («For Q5, session-
local declaration state and the collapsed additional-actions group are
implementation choices…») — не догадка, выданная за факт.
- Перечитан весь диапазон AC S1-19/S1-20 на предмет доказательства и негативного
свидетеля по каждому пункту (`git diff` выше) — оба присутствуют дословно
(«remove eligibility guard => …», «Require auto identity => generic-setup witness
fails», «persist declaration early => cancel-config comparison fails», «bypass
ACL or adapter proof => restricted-source/service canary fails»).
- Проверено, что решение M-1 из r3 не расширяет и не сужает исключения
`docs/SCOPE.md` (запись/тепловая карта/производные сущности) — дельта не трогает
`docs/SCOPE.md`, вопрос ограничен точкой входа в уже существующий редактор
устройства, не новым классом данных/хранения.
### Гейты
Диапазон затрагивает только `docs/specs/*.md` (class C); продуктовый код и тесты не
тронуты (`git diff --stat 21950437..2f2e3fca -- src custom_components` — пусто).
Validate зелёный на точном материале раунда (`2f2e3fca`,
https://github.com/Matysh/houseplan-card/actions/runs/34225567412, `success`,
запущен 2026-09-08T12:20:55Z) — дешёвые гейты (`typecheck`, `test`, `build` со
сверкой копий бандла) на этом SHA подтверждены и повторно не гонялись.
Прогнано самостоятельно, дёшево, напрямую по этому SHA (диапазон докс-only, эти
гейты применимы и не покрыты Validate по существу для этой дельты):
| Гейт | Команда | Результат |
|---|---|---|
| Markdown/diff-гигиена | `git diff --check 21950437..2f2e3fca -- docs/specs/485-radar-presence-stage1.md docs/specs/485-radar-presence.md` | чисто (exit 0) |
| Документационные проверки | `node scripts/check-docs.mjs` | `Documentation checks passed (7 files, 12 external links)` |
`tsc`/`test`/`build`/`invariants`/смоки/`golden`/`pytest`/перфопрофили — не
запускал: diff не касается `src/**`/`custom_components/**/*.py`, ни один AC этой
стадии не требует их прогона для spec-review, и Validate на этом же SHA уже
подтвердил дешёвые гейты репозитория в целом.
## Закрытие раунда r3
| Находка r3 | Чем закрыта | Где это видно |
|---|---|---|
| **M-1** — новый структурный гейт видимости исключал ранее принятый адаптер «любой числовой сенсор» (`range_v1`) без ручного пути настройки; продуктовая граница решена предположением автора, а не владельцем | Владелец подтвердил Q5-default: вторичное действие **«Это радар присутствия»** в свёрнутой группе «Additional actions» редактора устройства для любого нераспознанного реального устройства/сущности (включая standalone `sensor` без `device_id`); действие открывает тот же мастер с ручным выбором из `range_v1`/`cartesian_v1`/`polar_v1`/`zones_v1`/`presence_v1`, без авто-подстановки и без доп. диалога подтверждения. Основной раздел остаётся скрытым до явного выбора; Cancel/выход/скрытие страницы/смена привязки ничего не сохраняет и не переносит согласие на другое устройство. Сохраняется только валидированный целиком `marker.radar` по успешному атомарному Save. Добавлена новая AC S1-20 с доказательством и негативными свидетелями; WS/ACL-раздел явно уточнён: объявление не есть авторизация, per-source ACL и `may_write` проверяются сервером независимо. Добавлены i18n-ключи `additional_actions`/`declare_radar` (en+ru). | `docs/specs/485-radar-presence.md:54` (Q5 default в таблице), `:67-79` (§3 UX-абзац с новым действием); `docs/specs/485-radar-presence-stage1.md:48-51` (гейт видимости с явной оговоркой про Q5), `:71-79` (новый маркированный пункт «explicit… This is a presence radar action»), `:93-118` (раздел «Manual entry (Q5)» целиком), `:495-501` (уточнённый WS/ACL-абзац), `:524-525` (i18n), `:611-612` (AC S1-19 обновлена, AC S1-20 добавлена) |
Находка закрыта предметно: правка лежит ровно там, где r3 указала место
(«ручной путь-эскейп»), реализует ровно тот вариант, который r3 предложила первым
(«Добавить в §2 явный ручной путь… ведущий в тот же мастер… отдельное от двух
автоматических путей»), и количественно совпадает с формулировкой владельца — не
шире и не уже.
## Унаследовано из r3
Принято без повторной проверки в этом раунде, так как дельта их не задевает;
основание — [SPEC-REVIEW-485-r3](https://github.com/Matysh/houseplan-card/blob/21950437b4343deead48c955d06191ea15bb567a/docs/reviews/SPEC-REVIEW-485-r3.md)
на SHA `21950437b4343deead48c955d06191ea15bb567a` (и через неё — r2 на `f4bab4a4`,
r1 на `6550a69e`):
- Продуктовая рамка, границы этапов 1→2→3 и их отнесение к J1/`docs/SCOPE.md`.
- `docs/SCOPE.md`-исключения для производных HA-сущностей и записи/тепловой карты
(закрытые в r1→r2) — эта дельта их не расширяет и не сужает.
- Touch-декларация мастера калибровки этапа 1 (`best effort / intentionally
degraded`) и этапов 2/3 — не тронута.
- Единая модель идентичности (`source_generation`, `calibration_revision`,
`server_session_id`/`seq`), лимиты, WS-контракт §7 stage1 (кроме уточнённого
ACL-абзаца, разобранного в этом раунде).
- Полнота AC-таблиц C-1…C-8, S2-1…S2-22, S3-1…S3-18 и S1-1…S1-18 (кроме
обновлённой S1-19 и новой S1-20, разобранных в этом раунде).
- Правило скрытия раздела для обычных устройств (лампы/выключатели/температура/
обычный PIR/виртуальный маркер) и сохранение пути ремонта уже настроенного
радара.
- Точки входа этапов 2 и 3 (`stage2.md:75`, `stage3.md:86`) — повторно сверены в
этом раунде на предмет совместимости с новым ручным путём (см. «Как
проверялось»), новых противоречий не найдено.
- Матрицы совместимости/жизненного цикла, i18n-таблица §8 stage1 (кроме двух новых
ключей), перф-бюджеты §10 stage1.
- Ответы Q1–Q4 и их количественное соответствие тексту.
## Находки
Нет. Единственная блокирующая находка r3 (M-1) закрыта предметно и в точности по
предложенному варианту; дельта не вносит новых недекларированных догадок, не
ослабляет ACL/безопасность и не противоречит унаследованным AC.
## Что проверено и корректно
- Q5-default количественно и дословно совпадает между комментарием владельца,
общим контрактом (§3, таблица Q-defaults) и этапом 1 (§2, раздел «Manual entry
(Q5)») — три места не разошлись.
- Новый ручной путь явно не создаёт авторизации: WS/ACL-абзац требует независимой
проверки чтения каждого источника и `may_write` маркера сервером, объявление
«это радар» само по себе прав не даёт — закрывает очевидный риск privilege
escalation через самообъявление.
- Реализационные детали (расположение кнопки, отсутствие отдельного персистентного
флага) явно помечены как «assumed, change freely», а не выданы за факт.
- AC S1-20 имеет колонку доказательства и минимум три названных негативных
свидетеля (авто-идентичность обязательна → падает; ранняя персистентность →
падает; обход ACL/аппаратного адаптера → падает).
- i18n добавлен в обеих колонках (en/ru) в единственной таблице, второй копии не
создано (правило «один источник» не нарушено — новых пользовательских чисел
дельта не вводит вовсе).
- Точки входа этапов 2 и 3 остаются валидны при новом способе получения
«настроенного радара» — они читают уже сохранённый `marker.radar`, а не способ,
которым он появился.
- `docs/SCOPE.md` не требует правки для этой дельты — вопрос ограничен точкой входа
в существующий редактор устройства.
- Трейлеры коммита `2f2e3fca`: `Issue: #485`, `User-Visible: no` — верно для
докс-only изменения без видимого пользователю поведения (фича ещё не
реализована).
## Чего не проверял
- Не перепроверял полностью AC-таблицы C-1…C-8, S2-1…S2-22, S3-18, модель
идентичности, совместимость, перф-бюджеты, touch-декларации, продуктовую рамку —
дельта их не задевает; раздел «Унаследовано из r3» перечисляет, что принято без
повторной проверки.
- Не запускал `npx tsc --noEmit`, `npm test`, `npm run build`, `npm run
invariants`, браузерные смоки, `golden:verify`, `python -m pytest
tests_backend`, перфопрофили — diff докс-only, не касается
`src/**`/`custom_components/**/*.py`; ни один AC пакета не требует их прогона на
стадии spec-review; Validate на этом же SHA (`2f2e3fca`) подтверждает дешёвые
гейты репозитория в целом.
- Не проверял `docs/USER-GUIDE.ru.md`/`docs/USER-GUIDE.md` построчно — фича не
выпущена, руководство ещё не содержит раздела о радарах (не с чем сверять).
- Не оценивал реализуемость новых числовых/технических деталей WS-абзаца — они не
являются числовыми константами и не помечены как открытый вопрос; явных
внутренних противоречий не найдено при построчной сверке.
## Вердикт
**Зелёный.** High — 0, Medium — 0. Единственная находка r3 (M-1) закрыта предметно
и без расширения/сужения скоупа сверх решения владельца. Комплект (общий контракт
+ три этапа) готов к переходу S4 → S5 — с оговоркой владельца, повторённой в
хендоффе автора: **остановиться в S5-ready, реализацию не начинать**.
Этот зелёный вердикт цикла не образует и бюджет §4 не тратит (#227): было
израсходовано 2 из 4, после этого раунда — по-прежнему 2 из 4.
---
<!-- material-anchors: подготовлено ревьюером вручную (see PROCESS §2.10/#416); конвейер допишет машинный блок при публикации -->
## Материал раунда
- Ветка: `issue/485-radar-presence`, коммит `2f2e3fcaca98055b93e647fe8e11df0f1567f643`.
- ТЗ `docs/specs/485-radar-presence-stage1.md` на этом коммите.
- ТЗ `docs/specs/485-radar-presence.md` на этом коммите.
- ТЗ `docs/specs/485-radar-presence-stage2.md`, `docs/specs/485-radar-presence-stage3.md` — не изменены с r2 (блобы см. SPEC-REVIEW-485-r2.md/r3.md).
- Validate: https://github.com/Matysh/houseplan-card/actions/runs/34225567412 (`success`, headSha `2f2e3fca`).
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/485-radar-presence`, коммит `2f2e3fcaca98` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `a72dbb18c130b5f648a5e07570646e9fc2cdaf45`
```
git log --all --format='%H %T' | grep a72dbb18c130
```
- ТЗ `docs/specs/485-radar-presence-stage1.md`, блоб `c47c46bec46eda4be832809273fbe30de48cd9a8`
```
git log --all --find-object=c47c46bec46eda4be832809273fbe30de48cd9a8 -- docs/specs/485-radar-presence-stage1.md
```
- ТЗ `docs/specs/485-radar-presence-stage2.md`, блоб `b403e73fc26ba0ffb97be4ffa73be067d179880f`
```
git log --all --find-object=b403e73fc26ba0ffb97be4ffa73be067d179880f -- docs/specs/485-radar-presence-stage2.md
```
- ТЗ `docs/specs/485-radar-presence-stage3.md`, блоб `c65e28389d762216c47f00249bdb751f3c9d5082`
```
git log --all --find-object=c65e28389d762216c47f00249bdb751f3c9d5082 -- docs/specs/485-radar-presence-stage3.md
```
- ТЗ `docs/specs/485-radar-presence.md`, блоб `3297ed0140265ad86f2072a40cb9bf8097c0674d`
```
git log --all --find-object=3297ed0140265ad86f2072a40cb9bf8097c0674d -- docs/specs/485-radar-presence.md
```