mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 05:41:34 +00:00
@@ -0,0 +1,275 @@
|
||||
# SPEC-REVIEW-485-r3
|
||||
|
||||
Issue: [#485](https://github.com/Matysh/houseplan-card/issues/485).
|
||||
Материал: ветка `issue/485-radar-presence`, SHA `21950437b4343deead48c955d06191ea15bb567a`
|
||||
(baseline r2 материал `f4bab4a4bd5f4ac9b91cc16363985323a20ae907`, докс-only diff,
|
||||
проверено `git diff --stat` — только `docs/specs/485-radar-presence.md` и
|
||||
`docs/specs/485-radar-presence-stage1.md`; коммит `docs/reviews/SPEC-REVIEW-485-r2.md`
|
||||
самого документа r2 в дельту не входит, по прецеденту r2).
|
||||
Этап: **spec** (S4-spec-review). Заход: r3. Блокирующих циклов израсходовано 1 из 4
|
||||
(зелёный вердикт r2 цикл не образовал и бюджет не потратил, #227). Трек: полный
|
||||
(метка `small` отсутствует). Ревьюер ≠ автор.
|
||||
|
||||
## Скоуп разбора (по дельте, PROCESS §2.9/§2.10, issue #214)
|
||||
|
||||
Round r2 закрылся зелёным (High 0 / Medium 0) на материале `f4bab4a4`; findings
|
||||
предыдущего раунда закрывать нечего — этот раунд разбирает не исправление r2, а
|
||||
**новое уточнение владельца**, полученное уже после зелёного r2 (комментарий issue
|
||||
2026-09-08T11:30:04Z): раздел «Присутствие на плане» в редакторе устройства
|
||||
показывается только подходящим устройствам, ранее настроенным радарам сохраняется
|
||||
путь исправления; общий переключатель не меняется.
|
||||
|
||||
`git diff f4bab4a4..21950437 --stat` (без коммита самого документа r2):
|
||||
|
||||
```
|
||||
docs/specs/485-radar-presence-stage1.md | 66 ++++++++++++++++++++++++++++++---
|
||||
docs/specs/485-radar-presence.md | 15 ++++++--
|
||||
```
|
||||
|
||||
Формально дельта локальна по объёму (два файла, точечные абзацы в §2/§3 общего
|
||||
контракта и §2/§3/AC-таблицы этапа 1). Но по содержанию это не техническая правка
|
||||
и не декларативная фиксация ранее решённого — это **новый поведенческий контракт**
|
||||
(кто вообще видит точку входа в настройку радара), которого не было ни в r1, ни в
|
||||
r2. Согласно правилу §2.10 «разбор остаётся полным, если... смена контракта
|
||||
поведения» — я не ограничился построчной сверкой «что просил владелец / что
|
||||
дописано», а перечитал §3 обоих документов (модель источников/профилей) и точки
|
||||
входа в этапах 2–3, чтобы проверить, не рвёт ли новый гейт видимости то, что r1/r2
|
||||
уже приняли как рабочее. Остальное содержимое пакета (модель идентичности,
|
||||
совместимость, i18n, перф, этапы 2–3 целиком) дельта не задевает — там разбор по
|
||||
дельте, без повторного прохода (см. «Унаследовано из r2»).
|
||||
|
||||
## Как проверялось
|
||||
|
||||
- `gh issue view 485 --json labels,state` — метки `P2, feature, S4-spec-review`,
|
||||
`small` отсутствует: полный трек подтверждён, счётчик заходов/циклов совпадает
|
||||
с описанием задачи.
|
||||
- Построчный `git diff f4bab4a4..21950437` по обоим изменённым файлам (полный
|
||||
текст цитирую в разделе «Находки»).
|
||||
- Перечитано начало и конец тела issue #485 (`gh issue view 485 --json body`) —
|
||||
включая раздел «Что есть у easy-floorplan и что из этого брать» и его
|
||||
«Берём»/«Не берём» — источник профиля `range_v1`/«любой числовой сенсор».
|
||||
- Сверены точки входа в `docs/specs/485-radar-presence-stage2.md:75` и
|
||||
`docs/specs/485-radar-presence-stage3.md:86` («Device editor → Presence on plan
|
||||
→ …») — обе описывают вход из уже определённого/настроенного радара, новому
|
||||
гейту видимости не противоречат.
|
||||
- Проверено техническое утверждение stage1 §2 про «existing conditional vacuum
|
||||
section» как прецедент: `src/houseplan-editor-runtime.ts` действительно содержит
|
||||
условный рендер `renderVacuumMapsSection` (строка 10701, внутри `... ? html\`...
|
||||
${renderVacuumMapsSection(...)}\` : nothing`) — утверждение не выдумано.
|
||||
- `docs/UX-MODES.md` просмотрен на предмет канонического правила про
|
||||
показ/скрытие секций в редакторе устройства (в противовес «показывать, но
|
||||
дизейблить») — прямого правила, которое запрещало бы условный показ по типу
|
||||
устройства, не найдено; секция «пылесоса» — рабочий прецедент того же рода.
|
||||
- `docs/SCOPE.md` — дельта его не трогает; новый гейт видимости не расширяет и не
|
||||
сужает ранее записанное исключение (это вопрос UI-точки-входа в существующий
|
||||
редактор устройства, не новый класс данных/хранения).
|
||||
- Трейлеры коммита `21950437`: `Issue: #485`, `User-Visible: no` — верно для
|
||||
докс-only правки без видимого поведения.
|
||||
- Таблица i18n (§8 stage1) дельтой не тронута: новый гейт видимости не вводит
|
||||
собственного текста (ineligible-устройства просто не получают раздел), поэтому
|
||||
отсутствие нового ключа — не пропуск.
|
||||
|
||||
### Гейты
|
||||
|
||||
Диапазон затрагивает только `docs/specs/*.md` (class C), продуктовый код и тесты
|
||||
не тронуты (`git diff --stat origin/dev..HEAD -- src custom_components` — пусто).
|
||||
Прогнаны сам, дёшево и напрямую по этому SHA:
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| Markdown/diff-гигиена | `git diff --check f4bab4a4..HEAD -- docs/specs/485-radar-presence.md docs/specs/485-radar-presence-stage1.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 этой стадии их не
|
||||
требует (эта же логика применялась в r1/r2 и подтверждена авторским
|
||||
`git diff --stat`). Отдельный зелёный Validate на этом SHA для данного раунда не
|
||||
искал — предыдущий раунд (r2, `f4bab4a4`) уже подтвердил репозиторий в целом, а
|
||||
эта дельта докс-only и не расширяет исполняемую поверхность.
|
||||
|
||||
## Закрытие раунда r2
|
||||
|
||||
r2 закрылся с High 0 / Medium 0 — закрывать нечего, таблица «находка → чем
|
||||
закрыта» неприменима. Материал этого раунда — не фикс находок r2, а новое
|
||||
уточнение владельца, полученное после зелёного вердикта (см. «Скоуп разбора»).
|
||||
|
||||
## Унаследовано из r2
|
||||
|
||||
Принято без повторной проверки в этом раунде, так как дельта их не задевает;
|
||||
основание — [SPEC-REVIEW-485-r2](https://github.com/Matysh/houseplan-card/blob/f4bab4a4bd5f4ac9b91cc16363985323a20ae907/docs/reviews/SPEC-REVIEW-485-r2.md)
|
||||
на SHA `f4bab4a4bd5f4ac9b91cc16363985323a20ae907`:
|
||||
|
||||
- Продуктовая рамка, границы этапов 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.
|
||||
- Полнота AC-таблиц C-1…C-8, S2-1…S2-22, S3-1…S3-18 и S1-1…S1-18 (кроме новой
|
||||
S1-19, разобранной в этом раунде отдельно).
|
||||
- Матрицы совместимости/жизненного цикла, i18n-таблица §8 stage1, перф-бюджеты
|
||||
§10 stage1.
|
||||
- Ответы Q1–Q4 и их количественное соответствие тексту.
|
||||
|
||||
## Находки
|
||||
|
||||
### M-1 (Medium, в скоупе) — новый гейт видимости, по тексту, исключает ранее принятый адаптер «любой числовой сенсор» (класс `range`), не оставляя описанного пути его настроить
|
||||
|
||||
`docs/specs/485-radar-presence-stage1.md:55-59` (после дельты):
|
||||
|
||||
> A positive presence-radar device/profile match or a verified radar-specific
|
||||
> source-role descriptor on the exact owning device makes it eligible. Match
|
||||
> evidence is structural adapter/registry metadata, not a friendly-name search.
|
||||
|
||||
и `stage1.md:69-73`:
|
||||
|
||||
> A normal light, switch, temperature/humidity sensor, ordinary PIR, virtual
|
||||
> marker, or a device with merely arbitrary numeric/binary entities is not a
|
||||
> radar candidate. Two numeric values or a generic occupancy/motion class alone
|
||||
> do not prove a radar profile.
|
||||
|
||||
Оба условия — «структурные метаданные адаптера/реестра» и явное исключение
|
||||
«merely arbitrary numeric… entities» — требуют, чтобы устройство было
|
||||
**автоматически опознано** ещё до того, как раздел вообще появится. Ни здесь, ни
|
||||
где-либо ещё в дельте нет описанного пути «пользователь вручную объявляет
|
||||
устройство радаром», отличного от двух метаданных-путей.
|
||||
|
||||
Это прямо противоречит собственному решению задачи, зафиксированному в теле issue
|
||||
#485 (раздел «Что есть у easy-floorplan и что из этого брать», подтверждено
|
||||
владельцем 07.09 вместе с остальным «Решение владельца»):
|
||||
|
||||
> **Берём:** presence gate с fail-safe семантикой (сомнение → не показывать);
|
||||
> **«любой числовой сенсор» как низший общий знаменатель (класс «дальность»)**;
|
||||
> линия/дуга для одной оси; контур зоны только в редакторе; сглаживание между
|
||||
> отсчётами 1–4 Гц.
|
||||
|
||||
и в самом контракте (`stage1.md:144-151`, таблица профилей, не тронута дельтой):
|
||||
|
||||
> `range_v1` | One or two named distance channels, explicit length unit, optional
|
||||
> occupancy gate | Arc(s), not two people…
|
||||
|
||||
`range_v1` — это буквально «любой числовой сенсор» из принятого решения: датчики
|
||||
класса Tuya ZY-M100/TS0601, Aqara FP1E, Linptech ES1, Seeed MR24 и любой
|
||||
самодельный узел, перечисленные в самой постановке issue (раздел «Что радары умеют
|
||||
отдавать»), никак не опознаются «структурными метаданными адаптера» — у них нет
|
||||
признанного дескриптора вроде LD2450. До этой дельты пользователь мог зайти в
|
||||
раздел устройства и вручную назначить источники (`range_v1`/`cartesian_v1`/
|
||||
`polar_v1` — «generic explicit Cartesian/polar/range/occupancy inputs», common
|
||||
contract `485-radar-presence.md:86-88`, тоже не тронуто дельтой). После дельты
|
||||
раздел для такого устройства попадает под «merely arbitrary numeric… entities is
|
||||
not a radar candidate» и **не показывается вовсе** — то есть ручной путь входа,
|
||||
на котором держится вся «дальность»-ветка и вся идея «класса устройств, а не
|
||||
конкретной модели» (Суть issue: «Инструмент работы с любыми радарами присутствия…
|
||||
не конкретная модель, а класс устройств»), не имеет точки входа в UI.
|
||||
|
||||
AC S1-19 (новая, `stage1.md:558`) требует протестировать в том числе «range»-радар
|
||||
как один из допущенных типов — то есть сам автор рассчитывает, что такой путь
|
||||
существует, но текст §2 его не описывает.
|
||||
|
||||
**Почему это не мелочь, а не редактирование формулировки.** Уточнение владельца
|
||||
было одной строкой: «показывать только подходящим устройствам» — это про то, чтобы
|
||||
не засорять редактор лампочек и выключателей пустыми разделами. Реализация этой
|
||||
строки как «только структурно опознанные устройства» — решение автора, а не
|
||||
владельца, и оно тише удаляет из скоупа именно ту часть задачи (произвольный
|
||||
числовой датчик, взятый из easy-floorplan как «низший общий знаменатель»), которую
|
||||
до сих пор явно поддерживали r1 и r2. Это ровно тот случай из PROCESS §7.1:
|
||||
«поведение в пограничном случае» — граница между «опознанный радар» и «обычный
|
||||
датчик» — продуктовый вопрос, а не инженерный, и здесь он решён предположением,
|
||||
выданным за факт, без пометки «assumed, change freely» и без вопроса владельцу.
|
||||
|
||||
**Как чинится (любой из вариантов, в скоупе задачи):**
|
||||
1. Добавить в §2 явный ручной путь — например, действие «Это радар присутствия»
|
||||
на неопознанном устройстве, которое ведёт в тот же мастер с профилями
|
||||
`range_v1`/`cartesian_v1`/`polar_v1`, отдельное от двух автоматических путей и
|
||||
не открывающее раздел на самом деле неподходящих устройствах (лампах,
|
||||
выключателях) без явного действия пользователя; согласовать с «no friendly-name
|
||||
search» так, чтобы ручной путь не читался как автоматическое распознавание.
|
||||
2. Либо явно сузить AC S1-19 и профильную таблицу так, чтобы `range_v1`/`cartesian_v1`/
|
||||
`polar_v1` были помечены как доступные только через путь ремонта уже
|
||||
сохранённой конфигурации (т.е. фактически недостижимы для нового радара без
|
||||
признанного дескриптора), и одним пакетным вопросом с вариантом по умолчанию
|
||||
спросить владельца, допустимо ли это сужение — вопрос ровно того типа, который
|
||||
PROCESS §7.1 адресует владельцу («relates to what a person sees or does», здесь
|
||||
— сможет ли человек вообще настроить нераспознанный радар).
|
||||
|
||||
Находка в скоупе задачи (правится в этом же ТЗ, отдельный issue не заводится,
|
||||
#202).
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- Правило скрытия для обычных устройств (лампы/выключатели/температура/обычный
|
||||
PIR/виртуальный маркер) и сохранение пути ремонта для уже настроенного радара
|
||||
(в том числе при `unavailable`/неподдерживаемой версии) — сформулировано
|
||||
недвусмысленно, с исчерпывающим списком случаев и негативными свидетелями в AC
|
||||
S1-19 (`ordinary-device header assertion`, `offline/range/manual repair
|
||||
assertion`, `empty-installation settings assertion`).
|
||||
- Общий переключатель «Показывать присутствие на плане» остаётся видимым без
|
||||
радаров и не зависит от gate — текст (`stage1.md:108-110`,
|
||||
`485-radar-presence.md:69-71`) дословно совпадает с формулировкой уточнения
|
||||
владельца в issue.
|
||||
- Точки входа этапов 2 и 3 (`stage2.md:75`, `stage3.md:86`) остаются валидны при
|
||||
новом гейте: обе описывают вход из уже определённого/настроенного радара, не из
|
||||
произвольного устройства.
|
||||
- Новых i18n-ключей дельта не требует и не добавляет — раздел на неподходящих
|
||||
устройствах просто отсутствует, не превращается в текст с пустым состоянием.
|
||||
- Техническое утверждение про существующий условный раздел пылесоса как прецедент
|
||||
подтверждено чтением `src/houseplan-editor-runtime.ts:10701` — не выдумано.
|
||||
- `docs/SCOPE.md` не требует правки для этой дельты: вопрос — точка входа в
|
||||
существующий редактор устройства, не новый класс данных/хранения.
|
||||
- Трейлеры коммита корректны; дельта не касается `src/**`/`custom_components/**/*.py`.
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- Не перепроверял AC-таблицы C-1…C-8, S2-1…S2-22, S3-1…S3-18 и S1-1…S1-18 (кроме
|
||||
новой S1-19), модель идентичности, совместимость, перф-бюджеты, WS-контракт —
|
||||
дельта их не задевает; раздел «Унаследовано из r2» перечисляет, что принято без
|
||||
повторной проверки.
|
||||
- Не запускал `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.
|
||||
- Не проверял `docs/USER-GUIDE.ru.md`/`docs/USER-GUIDE.md` построчно на предмет
|
||||
терминологии — фича не выпущена, руководство ещё не содержит раздела о радарах
|
||||
(`grep` по обоим файлам — без совпадений), сверять не с чем.
|
||||
- Не оценивал остальные два варианта устранения M-1 на предмет объёма следующего
|
||||
раунда — это решение автора и, при необходимости, владельца.
|
||||
|
||||
## Вердикт
|
||||
|
||||
**Жёлтый.** High — 0, Medium — 1 (M-1, в скоупе задачи, чинится в этом же issue,
|
||||
#202). Находка блокирует переход S4 → S5: новый гейт видимости, введённый этой
|
||||
дельтой, тише сужает ранее принятое решение «любой числовой сенсор — класс
|
||||
`range`» без пометки предположения и без вопроса владельцу. Остальная часть
|
||||
пакета (унаследованная из r2) находок не имеет.
|
||||
|
||||
Этот жёлтый вердикт образует цикл и расходует бюджет §4: было израсходовано 1 из
|
||||
4, после этого раунда — 2 из 4.
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/485-radar-presence`, коммит `21950437b434` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `717b51fb35f3c98731eb72e53d0cc2ab87db25d4`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 717b51fb35f3
|
||||
```
|
||||
- ТЗ `docs/specs/485-radar-presence-stage1.md`, блоб `c5d9960cfaa0fa5573e58d384e8e2b7eeddc6fcc`
|
||||
```
|
||||
git log --all --find-object=c5d9960cfaa0fa5573e58d384e8e2b7eeddc6fcc -- 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`, блоб `dfc1edc3aa10d19c16eb37e1ddacf7c81c439216`
|
||||
```
|
||||
git log --all --find-object=dfc1edc3aa10d19c16eb37e1ddacf7c81c439216 -- docs/specs/485-radar-presence.md
|
||||
```
|
||||
Reference in New Issue
Block a user