mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-30 19:58:50 +00:00
@@ -0,0 +1,251 @@
|
||||
# SPEC-REVIEW-485-r1
|
||||
|
||||
Issue: [#485](https://github.com/Matysh/houseplan-card/issues/485).
|
||||
Материал: ветка `issue/485-radar-presence`, SHA `6550a69e6857d11aba69b65809f140d239245e88`
|
||||
(baseline `dev@ed9ee026dc08054e12038b7dbd1b8525e7706d04`, докс-only diff, проверено
|
||||
`git diff --stat` — только `docs/specs/485-radar-presence*.md` и `docs/specs/README.md`).
|
||||
Этап: **spec** (S4-spec-review). Заход: r1. Трек: полный (issue не помечен `small`).
|
||||
Ревьюер ≠ автор (роли закреплены в AGENTS.md).
|
||||
|
||||
## Скоуп разбора
|
||||
|
||||
Комплект из четырёх документов, рассматриваемых как единое ТЗ (общий контракт +
|
||||
три этапа), в порядке, предписанном §1 общего контракта:
|
||||
|
||||
1. `docs/specs/485-radar-presence.md` — общий контракт (акцептанс-карта C-1…C-8).
|
||||
2. `docs/specs/485-radar-presence-stage1.md` — источники, калибровка, живое присутствие (S1-1…S1-18).
|
||||
3. `docs/specs/485-radar-presence-stage2.md` — зоны, запись наблюдений, отражатели (S2-1…S2-22).
|
||||
4. `docs/specs/485-radar-presence-stage3.md` — покрытие, слияние, HA-сущности, тепловая карта (S3-1…S3-18).
|
||||
|
||||
Задача первого раунда (r1): разбор полный, второй раунд не образует делту (§2.10
|
||||
не применяется). Прочитаны целиком все четыре документа, `docs/SCOPE.md`,
|
||||
`AGENTS.md`, `PROCESS.md`, `docs/TOUCH-SUPPORT.md`, `docs/CONFIG-COMPATIBILITY.md`,
|
||||
тело issue #485 и все 6 комментариев (полевые данные автора запроса, UX-проработка
|
||||
владельца, аналитика S2/S3, подтверждение defaults Q1–Q4, публикация комплекта).
|
||||
|
||||
## Как проверялось
|
||||
|
||||
- Продуктовая рамка: сверено с `docs/SCOPE.md` — J1 и явно перечисленный
|
||||
допустимый gap («Person/presence shown in rooms… pure J1») закрывают живой слой;
|
||||
запись/облако/тепловая карта заявлены в общем контракте как «bounded exception»
|
||||
к исключению «History, graphs, statistics» — см. находку M-1.
|
||||
- Процесс: трек, статус, число заходов и трейлеры issue сверены через
|
||||
`gh issue view 485` (метки `P2, feature, S4-spec-review`, `small` отсутствует —
|
||||
полный трек подтверждён).
|
||||
- Технический разбор: каждый AC C/S1/S2/S3 прочитан вместе со своей строкой
|
||||
доказательства и негативным свидетелем; проверялась связность между документами
|
||||
(единая модель source_generation/calibration_revision, единые лимиты 32 радара,
|
||||
единая канонической проекция §5.1 этапа 1, единый предикат зон/отражателей).
|
||||
- Cross-check ссылок: `grep` подтвердил, что все четыре документа ссылаются друг
|
||||
на друга по существующим именам файлов (см. ниже), запись в `docs/specs/README.md`
|
||||
добавлена и указывает на все четыре файла.
|
||||
- `docs/TOUCH-SUPPORT.md` построчно сверен с требованием «New editor feature
|
||||
specifications … must state one of: Touch editor: supported / best effort /
|
||||
intentionally degraded / not exposed» — см. находку M-2.
|
||||
- Прецедент оформления SCOPE-исключений сверен по истории: `git log -- docs/SCOPE.md`
|
||||
показал, что оба предыдущих узких исключения (#89 «revise isometric stage 1
|
||||
specification», #53 «address PDF export spec review») были зафиксированы именно
|
||||
в `docs/SCOPE.md` в рамках цикла ревью/доработки спецификации, а не отложены до
|
||||
релиза — прямой прецедент для M-1.
|
||||
|
||||
### Гейты
|
||||
|
||||
Диапазон (`ed9ee026`..`6550a69e`) — только четыре файла в `docs/specs/`, докс-only
|
||||
(class C, `docs/specs/485-radar-presence*.md`), продуктовый код не тронут.
|
||||
Соответственно раннер-гейты `typecheck/test/build`, `check-docs.mjs`,
|
||||
`model-invariants`, browser-смоки, `golden:verify`, `pytest tests_backend` и
|
||||
перфопрофили — **не запускались, потому что неприменимы**: AC этого этапа не
|
||||
требуют исполнения кода (единственный AC, требующий факта — C-8, «diff restricted
|
||||
to documentation», проверен `git diff --stat` выше). Ссылка на зелёный Validate на
|
||||
этом SHA (`https://github.com/Matysh/houseplan-card/actions/runs/34218729737`)
|
||||
подтверждает, что дешёвые гейты тоже зелёные, хотя для этого раунда они не несут
|
||||
доказательной нагрузки — С4/АС ревью ТЗ не про исполнение кода. Никакой гейт не
|
||||
был пропущен «по недосмотру»: полный список выше — это список неприменимых, а не
|
||||
непрогнанных гейтов.
|
||||
|
||||
## Находки
|
||||
|
||||
### M-1 (Medium, в скоупе) — исключение из SCOPE.md для истории/тепловой карты не зафиксировано в самом SCOPE.md
|
||||
|
||||
`docs/SCOPE.md` явно относит «History, graphs, statistics» к Out of scope: «We
|
||||
show now, not then» (docs/SCOPE.md:98). Общий контракт (`485-radar-presence.md:40-46`)
|
||||
признаёт это прямо: «The owner explicitly requested zones, local observations,
|
||||
day/week heat maps… These are a bounded exception to SCOPE's exclusion of general
|
||||
historical analytics», но это утверждение живёт только в ТЗ. Сам `docs/SCOPE.md`
|
||||
никак не упоминает радары/присутствие/тепловые карты — ни в «Out of scope», ни в
|
||||
«Known gaps», хотя раздел «Known gaps» уже содержит близкую строку («Person/presence
|
||||
shown in rooms… pure J1», docs/SCOPE.md:64), которая закрывает **живой** слой (этап 1),
|
||||
но не объясняет исключение для записи/тепловой карты (этапы 2–3).
|
||||
|
||||
Прецедент есть в самом репозитории: оба прежних узких исключения из «Out of scope»
|
||||
— трёхмерный вид (#89) и печатный PDF-экспорт (#53) — были дописаны в
|
||||
`docs/SCOPE.md` именно в рамках доработки спецификации по итогам ревью
|
||||
(`74b08df8 docs: revise isometric stage 1 specification`, `299629d2 docs: address
|
||||
PDF export spec review`), а не отложены до релиза. Формат этих записей —
|
||||
короткий абзац «A narrow exception approved for #NN…» с явной границей, что именно
|
||||
разрешено и что нет (docs/SCOPE.md:101-114).
|
||||
|
||||
Ни один из трёх этапов не перечисляет `docs/SCOPE.md` среди release-артефактов
|
||||
(stage1 §10, stage2 §12, stage3 §14 называют CHANGELOG, USER-GUIDE, ARCHITECTURE,
|
||||
CONFIG-COMPATIBILITY, field registry — SCOPE.md нигде).
|
||||
|
||||
**Почему это не мелочь.** `docs/SCOPE.md` — заявленный «guard rail»: «features are
|
||||
built… only if they serve a job listed here» и «if there is none — it belongs to
|
||||
HA core, to another card, or nowhere» (docs/SCOPE.md:3-6). Пока запись/тепловая
|
||||
карта не зафиксированы как узкое исключение непосредственно в этом документе,
|
||||
следующий читатель SCOPE.md увидит прямое противоречие («history… we show now, not
|
||||
then») без объяснения, почему радар #485 — не такое же нарушение, каким было бы
|
||||
любое другое предложение исторической аналитики. Это ровно тот разрыв, который
|
||||
review #89/#53 закрывал на этой же стадии процесса.
|
||||
|
||||
**Как чинится (в скоупе):** добавить в `docs/SCOPE.md` абзац по образцу
|
||||
#89/#53 — узкое исключение для #485, ограниченное явно описанными в общем
|
||||
контракте рамками (запись ≤24 ч по явному согласию, тепловая карта — только
|
||||
агрегаты ≤7 дней, редактор-only доступ, никакого автопродления/непрерывного
|
||||
сбора), и добавить `docs/SCOPE.md` в перечень release-артефактов во всех трёх
|
||||
этапных документах (сейчас перечислены только CHANGELOG/USER-GUIDE/ARCHITECTURE/
|
||||
CONFIG-COMPATIBILITY/field registry).
|
||||
|
||||
### M-2 (Medium, в скоупе) — этап 1 не декларирует touch-классификацию нового редактора (обязательное требование TOUCH-SUPPORT.md)
|
||||
|
||||
`docs/TOUCH-SUPPORT.md` («Documentation rule», конец файла) требует: «New editor
|
||||
feature specifications and code reviews must state one of: `Touch editor:
|
||||
supported`; `Touch editor: best effort / intentionally degraded`; `Touch editor:
|
||||
not exposed`.» Это не общее пожелание, а обязательная декларация для каждой новой
|
||||
функции редактора.
|
||||
|
||||
Этап 2 и этап 3 её дают дословно:
|
||||
- `485-radar-presence-stage2.md:174` — «Desktop is the reference editor. Touch
|
||||
editor: best effort / intentionally degraded…»;
|
||||
- `485-radar-presence-stage3.md:131` — «Touch editor: **best effort /
|
||||
intentionally degraded** for precise geometry and pair selection…».
|
||||
|
||||
Этап 1 вводит новый мастер настройки радара (§5.2: «Installation» → «Two reference
|
||||
positions» → «Check») — это новая функция редактора не меньшего масштаба, чем зоны
|
||||
или анализ покрытия, но нигде в документе (проверено `grep -n -i touch` по всему
|
||||
файлу) не встречается требуемая формулировка. Единственные следы — общая фраза
|
||||
общего контракта §6 («Editors remain desktop-first… safe cancellation/permissions
|
||||
on touch») и AC S1-18 («safe desktop/touch exit meet §2/5/8»), которые описывают
|
||||
безопасный выход, а не классификацию поддержки самого мастера.
|
||||
|
||||
**Почему это не формальность именно здесь.** Шаг «Two reference positions»
|
||||
физически требует, чтобы пользователь **отошёл от компьютера** и встал в отмеченную
|
||||
точку комнаты, пока идёт 10-секундный отсчёт и 5-секундный захват (§5.2). Это,
|
||||
в отличие от обычного рисования зоны мышью, сценарий, где телефон/планшет в руке —
|
||||
скорее естественный инструмент, чем компромисс. Продуктовое решение «оставить это
|
||||
desktop-first best-effort, как и остальные редакторы» может быть правильным, но
|
||||
оно не сделано явно и не объяснено — редактор просто пропущен в классификации,
|
||||
а не намеренно отнесён к одному из трёх состояний. Раздел 5 (Compatibility/touch)
|
||||
из DoR-чеклиста PROCESS §2.5 требует именно этого явного решения, а не умолчания.
|
||||
|
||||
**Как чинится (в скоупе):** добавить в `485-radar-presence-stage1.md` (рядом с §5.2
|
||||
или в UX-разделе §6 общего контракта, где он уже цитируется по стадиям 2/3) точную
|
||||
формулировку `Touch editor: …` для мастера калибровки, с явным решением — либо
|
||||
«best effort / intentionally degraded» с тем же обоснованием, что у этапов 2–3, либо
|
||||
отдельно объяснить, почему шаг «дойти и встать» продуктово не требует лучшей
|
||||
touch-поддержки, чем обычное редактирование геометрии.
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- **Границы скоупа и не-скоупа** каждого этапа (Included/Excluded) не пересекаются
|
||||
и не противоречат друг другу: живой слой/калибровка (1) → зоны/запись/отражатели
|
||||
(2) → покрытие/слияние/сущности/тепловая карта (3); каждый этап явно говорит,
|
||||
что не берёт на себя работу другого («Excluded… specified in stages 2–3»).
|
||||
- **Единая модель идентичности** (`source_generation`, `calibration_revision`,
|
||||
`server_session_id`/`seq`) определена один раз в общем контракте §4 и
|
||||
последовательно используется во всех трёх этапах без переопределения смысла —
|
||||
проверено построчным сравнением определений в stage1 §6, stage2 §7.1, stage3 §10.
|
||||
- **AC и негативные свидетели.** Все 8+18+22+18=66 критериев приёмки имеют
|
||||
колонку доказательства и явно названный негативный сценарий/мутацию, которая
|
||||
должна дать красный результат (например S2-17: «31 of 100» с конкретной
|
||||
фикстурой из полевых данных 0.5%→31%, а не абстрактное «score correct»).
|
||||
Это выше формального минимума ТЗ (PROCESS требует только «чем доказывается»),
|
||||
но снижает риск немой защиты на код-ревью (#435).
|
||||
- **«Одно число — один источник»** для чисел, которые пользователь видит дважды:
|
||||
stage3 §7.2 прямо требует «native state must match the backend room snapshot»;
|
||||
оценка зоны (stage2 §4.3/§10) явно завязана на одну и ту же выборку для
|
||||
числителя и знаменателя. Нарушений правила PROCESS §8 не найдено.
|
||||
- **Совместимость и жизненный цикл** (импорт/дублирование/удаление комнаты/смена
|
||||
пространства/понижение фронтенда) описаны для каждого нового поля в каждом
|
||||
документе с явной матрицей «old/new frontend×backend», по образцу существующего
|
||||
`docs/CONFIG-COMPATIBILITY.md`; ни одного поля не найдено без описанной судьбы
|
||||
при откате.
|
||||
- **Продуктовые вопросы закрыты владельцем.** Q1–Q4 из UX-проработки получили
|
||||
явное подтверждение владельца в комментарии `IC_kwDOTOcLQM8AAAABTNOxFA`
|
||||
(08.09.2026); ни в одном документе не найдено скрытой догадки, выданной за
|
||||
факт, кроме двух находок выше — остальные технические допущения (имена
|
||||
модулей, тайминги, пороги солвера) прямо помечены «assumed, change freely»
|
||||
и корректно не эскалированы владельцу (PROCESS §7.1).
|
||||
- **C-8 (докс-only)**: `git diff --stat ed9ee026..6550a69e` подтверждает, что
|
||||
изменены только четыре файла `docs/specs/*` плюс запись в README реестра
|
||||
спецификаций — продуктовый код, тесты и версии не тронуты, ревью не требует
|
||||
прогона исполняемых гейтов.
|
||||
- **Перекрёстные ссылки.** Все четыре документа ссылаются друг на друга по
|
||||
существующим относительным путям (`grep` подтвердил отсутствие битых ссылок);
|
||||
запись в `docs/specs/README.md` добавлена и указывает на все три этапа плюс
|
||||
общий контракт.
|
||||
- **Touch-декларация этапов 2 и 3** присутствует дословно в требуемом формате
|
||||
(см. находку M-2 для контраста с этапом 1).
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- Не запускал `npx tsc --noEmit`, `npm test`, `npm run build`,
|
||||
`node scripts/check-docs.mjs`, `npm run invariants`, браузерные смоки,
|
||||
`golden:verify`, `pytest tests_backend`, перфопрофили — diff не касается
|
||||
`src/**`, `custom_components/**/*.py` ни какого-либо исполняемого артефакта;
|
||||
ни один AC этого пакета не требует их прогона для стадии spec-review. Зелёный
|
||||
Validate на этом SHA (ссылка выше) относится к дешёвым гейтам репозитория в
|
||||
целом, а не является доказательством для конкретных AC C-1…C-8/S1…S3 — эти AC
|
||||
про будущую реализацию, а не про текущий (докс-only) коммит.
|
||||
- Не оценивал реализуемость каждой числовой константы (тайминги, пороги солвера,
|
||||
лимиты хранения) на предмет технической достижимости — все они прямо помечены
|
||||
в документах как «engineering assumptions, change freely in review» (общий
|
||||
контракт §9, stage2 §13, stage3 §15), то есть по PROCESS §7.1 это зона, которую
|
||||
ревьюер вправе оспорить по существу, но не обязан подтверждать формулами; явных
|
||||
внутренних противоречий в константах не найдено при построчной сверке.
|
||||
- Не проверял согласованность предлагаемых имён Python/TS модулей с реальными
|
||||
соглашениями кодовой базы (это тоже явно помечено как «technical choices…
|
||||
change freely»), кроме одной точечной проверки существования `admin_only`/
|
||||
`CONF_ADMIN_ONLY` в `custom_components/houseplan/{auth,const}.py` — подтверждено,
|
||||
что константа не выдумана.
|
||||
- Английская версия `docs/USER-GUIDE.md` не сверялась построчно (только
|
||||
`docs/USER-GUIDE.ru.md` упомянут как источник терминологии в AGENTS.md); ни один
|
||||
из трёх документов не вводит видимого текста, требующего сверки терминов, кроме
|
||||
таблиц i18n `radar.*`, которые самодостаточны и не переиспользуют существующие
|
||||
строки интерфейса не по смыслу — насколько можно судить без реализации.
|
||||
|
||||
## Вердикт
|
||||
|
||||
**Жёлтый.** Оба High отсутствуют; две находки Medium — обе в скоупе задачи и
|
||||
чинятся правкой самого ТЗ-комплекта (SCOPE.md-исключение + touch-декларация для
|
||||
этапа 1), без отдельного issue. Комплект в остальном исключительно полон для
|
||||
своего масштаба: акцептанс-карта, негативные свидетели, совместимость и
|
||||
продуктовые границы проработаны заметно выше типичного порога DoR.
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/485-radar-presence`, коммит `6550a69e6857` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `56ba25bd189cba041dbfaca20f6717b8cb9bcb14`
|
||||
```
|
||||
git log --all --format='%H %T' | grep 56ba25bd189c
|
||||
```
|
||||
- ТЗ `docs/specs/485-radar-presence-stage1.md`, блоб `2e007f56b5d52fb3537eac42ed134f52f27944c5`
|
||||
```
|
||||
git log --all --find-object=2e007f56b5d52fb3537eac42ed134f52f27944c5 -- docs/specs/485-radar-presence-stage1.md
|
||||
```
|
||||
- ТЗ `docs/specs/485-radar-presence-stage2.md`, блоб `46178cfa4c26e1c5b48d766a5a5ba47e909158b7`
|
||||
```
|
||||
git log --all --find-object=46178cfa4c26e1c5b48d766a5a5ba47e909158b7 -- docs/specs/485-radar-presence-stage2.md
|
||||
```
|
||||
- ТЗ `docs/specs/485-radar-presence-stage3.md`, блоб `bc4e8cd7675f181dc308ec01de5e6a980c34f9f4`
|
||||
```
|
||||
git log --all --find-object=bc4e8cd7675f181dc308ec01de5e6a980c34f9f4 -- docs/specs/485-radar-presence-stage3.md
|
||||
```
|
||||
- ТЗ `docs/specs/485-radar-presence.md`, блоб `8b56afbdbb5923e002513a31e05a83563e5d0c02`
|
||||
```
|
||||
git log --all --find-object=8b56afbdbb5923e002513a31e05a83563e5d0c02 -- docs/specs/485-radar-presence.md
|
||||
```
|
||||
Reference in New Issue
Block a user