mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
@@ -0,0 +1,260 @@
|
||||
# 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
|
||||
```
|
||||
Reference in New Issue
Block a user