mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-01 04:09:17 +00:00
@@ -0,0 +1,230 @@
|
||||
# SPEC-REVIEW-196-r1
|
||||
|
||||
- **Issue:** https://github.com/Matysh/houseplan-card/issues/196
|
||||
- **ТЗ под ревью:** тело issue #196 (лёгкий трек `small`, файл в `docs/specs/`
|
||||
отсутствует — корректно, `docs/specs/196-*.md` не создан)
|
||||
- **Роль:** ревьюер ТЗ (не автор), этап `S4-spec-review`
|
||||
- **Трек:** лёгкий (`small`), лимит циклов ревью ТЗ — 2 (§4, §5 PROCESS.md)
|
||||
- **Цикл:** r1/2
|
||||
|
||||
## Скоуп ревью
|
||||
|
||||
ТЗ #196: room hover tooltip в View получает необязательную строку «средняя
|
||||
влажность: N%» между температурой и LQI. Значение берётся из уже существующего
|
||||
`_roomHum()` (tier-3 `hum_source` → среднее по HA-зоне из `_climate()`),
|
||||
симметрично уже реализованной температуре. Device tooltip, room card/label
|
||||
(влажность там уже есть), touch-эквивалент hover, backend, схема, миграция —
|
||||
вне скоупа по тексту ТЗ.
|
||||
|
||||
Ветки `issue/196-*` в репозитории ещё нет — ожидаемо: лёгкий трек, задача
|
||||
только что вышла из аналитики в ревью ТЗ, продуктового кода не существует.
|
||||
Гейты (`typecheck`/`test`/`build`, smoke) не прогонялись: на этом этапе им
|
||||
неоткуда взяться (PROCESS.md §2.4/§8).
|
||||
|
||||
## Как проверялось
|
||||
|
||||
1. Прочитаны целиком `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` (действующая
|
||||
редакция, включая §1, §2.4, §5, §7.1, §8).
|
||||
2. Прочитано тело issue #196 целиком и все три комментария: аналитика
|
||||
владельца (ценность 4/10 · сложность 2/10 · P2 · `enhancement` ·
|
||||
поверхности `src/houseplan-card.ts` + i18n + smoke/docs · трек `small`,
|
||||
`trivial` явно отклонён из-за нового i18n-ключа) и два «Взял:» на роли
|
||||
аналитика/автора ТЗ.
|
||||
3. Построчно сверены все технические утверждения раздела «Сейчас» и
|
||||
«Контракт» с кодом `origin/dev` (`src/houseplan-card.ts`, 19891 строка):
|
||||
- `_tip` / `_showTip()` — `houseplan-card.ts:713` и `:5758`: сигнатура
|
||||
`(ev, title, meta, lqi?, temp?)`, поле `_tip` уже несёт `lqi?: number |
|
||||
null; temp?: number | null`. Добавление `hum?: number | null` последним
|
||||
параметром — прямое расширение без переупорядочивания существующих.
|
||||
- Room hover передаёт `_showTip(e, name, area, lqi, this._roomTemp(r))` —
|
||||
`houseplan-card.ts:15627-15633` (в ТЗ заявлено `:15621`, расхождение на
|
||||
6 строк из-за коммитов после написания ТЗ — не дефект, код найден и
|
||||
совпадает буквально).
|
||||
- Рендер тултипа — `houseplan-card.ts:15863-15874` (в ТЗ `:15860`, то же
|
||||
смещение): строка `tip.temp` идёт первой (`if (this._tip.temp != null)`),
|
||||
затем `tip.lqi` — вставка `hum` между ними ровно там, где просит
|
||||
контракт п.3, синтаксически тривиальна (тот же паттерн `!= null ? html…
|
||||
: nothing`).
|
||||
- Device tooltip — `houseplan-card.ts:16729-16731`: вызывает `_showTip(e,
|
||||
d.name, metrics)` — **без** `lqi`/`temp`/`hum` вовсе. Подтверждает
|
||||
заявление контракта п.1 «existing device call остаётся семантически
|
||||
неизменным»: добавление пятого необязательного параметра ничего здесь
|
||||
не меняет, TS не потребует правки этого call site.
|
||||
- `_roomTemp()` / `_roomHum()` — `houseplan-card.ts:16769-16781`:
|
||||
функции буквально симметричны («tier-3 `temp_source`/`hum_source` →
|
||||
иначе `_climate().get(area)`»). `_roomHum()` уже существует и уже
|
||||
используется в room label (`:17085-17086`, `${hm}%` — тот же формат
|
||||
значения, что просит ТЗ). Ссылка на реальный существующий helper, а не
|
||||
на функцию, которую ещё нужно написать.
|
||||
- `_climate()` — `houseplan-card.ts:16792-16802`: один кеш на `hass`
|
||||
identity, комментарий на `:16790` дословно говорит «two, with humidity
|
||||
on» (в ТЗ упомянут как `:16784`, тот же класс смещения) — подтверждает
|
||||
п.7 контракта: чтение влажности не создаёт нового прохода по registry.
|
||||
- `_noHover` — `houseplan-card.ts:5652-5653`, используется как guard
|
||||
первой строкой `_showTip()` (`:5759`) — подтверждает контракт п.6:
|
||||
`_showTip` уже отказывается строить тултип на no-hover устройствах,
|
||||
новая строка не создаёт новую touch-поверхность просто потому, что
|
||||
`hum` передаётся тем же вызовом.
|
||||
- Значение уже целое: `areaClimateMap` округляет `hum` до `Math.round(...)`
|
||||
(`src/devices.ts:1396`), `sourceValue()` для явного `entity:`/`device:`
|
||||
источника — тоже `Math.round(v)` (`src/devices.ts:1252, humFor`).
|
||||
Предположение ТЗ «значение не пересчитывается, используется готовый
|
||||
результат `_roomHum()` с суффиксом `%`» корректно: дробного значения
|
||||
физически не бывает, формат `${hum}%` без округления в тултипе не
|
||||
потеряет точность.
|
||||
4. Прочитан `src/i18n/en.json` и `ru.json`: `tip.temp_avg` (`:374`
|
||||
`"average temperature:"` / `"средняя температура:"`) и `tip.lqi`
|
||||
(`:150`) — предложенный `tip.hum_avg` (`"average humidity:"` /
|
||||
`"средняя влажность:"`) следует тому же формату (двоеточие, строчная
|
||||
буква, `average <metric>:`), новый ключ не выдуман по структуре.
|
||||
5. Прочитан `docs/USER-GUIDE.ru.md`: строка 173 (таблица режимов) —
|
||||
«подсказка показывает название, чистую площадь, температуру и LQI» — это
|
||||
ровно то место, которое ТЗ обязано обновить (и заявляет это в разделе
|
||||
Release), и ровно то место, что фиксирует нынешний пробел (влажности в
|
||||
перечислении нет, хотя строка 128/275/1175 подтверждает, что влажность
|
||||
уже показывается в room card). Строка 1310 «Комнатные подсказки основаны
|
||||
на hover» — существующее, не создаваемое этим ТЗ ограничение,
|
||||
распространяется на новую строку тем же образом, что и на текущие.
|
||||
6. Прочитан `docs/TOUCH-SUPPORT.md` целиком. Правило «Documentation rule»
|
||||
(`Touch editor: …`) относится к «New editor feature specifications» —
|
||||
тултип комнаты живёт в View, не в редакторе, правило не применяется
|
||||
механически. Контракт «fully supported View» (строка 31-32: «provide a
|
||||
touch path for essential information that desktop exposes through
|
||||
hover») для влажности не создаёт нового риска: `disp.labelHum` уже кладёт
|
||||
влажность в room label (`houseplan-card.ts:17084-17086`), который виден
|
||||
без hover — то есть на touch влажность уже доступна отдельным путём,
|
||||
ТЗ не ухудшает существующий (уже документированный как ограничение,
|
||||
`USER-GUIDE.ru.md:1310`) hover-only статус тултипа для area/temp/LQI.
|
||||
7. Проверено, что `docs/specs/196-*.md` не существует (`ls docs/specs/` —
|
||||
пусто по маске) — соответствует лёгкому треку.
|
||||
8. Проверено соответствие `docs/SCOPE.md`: задача закрывает J5 «Room climate
|
||||
at a glance» (persona home admin/household, View), не расширяет
|
||||
out-of-scope списки, не касается lock invariant, не создаёт нового job'а.
|
||||
9. Проверены оба существующих файла-кандидата для smoke:
|
||||
`demo/smoke_ux_fixes.mjs` (`:21-27`) уже содержит инфраструктуру для
|
||||
проверки `_tip.temp` и текстового содержимого `.tip` («средняя
|
||||
температура»), то есть AC1-AC3 технически осуществимы без нового
|
||||
тестового харнесса — паттерн для новой `hum`-проверки уже есть в файле.
|
||||
`demo/srv/demo.html` не содержит фикстуры-сенсора влажности; но 31 файл
|
||||
в `demo/` (в т.ч. `smoke_room_settings.mjs`, `smoke_sun.mjs` и др.) уже
|
||||
инжектируют синтетические `hass.states` динамически в `page.evaluate` —
|
||||
значит отсутствие штатной humidity-фикстуры не блокирует AC1/AC2,
|
||||
разработчик может ввести сенсор тем же приёмом. Это техническое решение,
|
||||
ТЗ корректно оставляет выбор конкретных имён/assertions разработчику
|
||||
(раздел «Принятые технические предположения»).
|
||||
|
||||
## Обязательные разделы для лёгкого трека (§5 PROCESS.md)
|
||||
|
||||
| Раздел | Есть | Комментарий |
|
||||
|---|---|---|
|
||||
| Проблема | ✅ | Подтверждена построчным чтением кода (см. «Как проверялось» п.3): `_roomHum()` существует и используется в label, но не в tooltip — не догадка, факт |
|
||||
| Контракт | ✅ | 7 пронумерованных пунктов, включая явную защиту от расширения device tooltip (п.1) и touch-поверхности (п.6) |
|
||||
| AC1…ACn с доказательством | ✅ | 5 штук, у каждого назван способ (`smoke`/`unit`+`build`/«ревью кода») |
|
||||
| Откат | ✅ | «Совместимость и откат»: revert одного коммита, без миграции, без cleanup |
|
||||
|
||||
Дополнительно присутствуют разделы, не обязательные для лёгкого трека, но
|
||||
усиливающие ТЗ: «Не входит в задачу» (явный анти-скоуп), «Файлы и проверки»,
|
||||
«Release, performance, security, visual», «Принятые технические
|
||||
предположения» (3 пункта, все технические — не маскируют продуктовый вопрос).
|
||||
|
||||
## Находки
|
||||
|
||||
### Low-1 — AC2 частично привязывает `smoke`-доказательство к утверждению, проверяемому только кодом
|
||||
|
||||
**Место:** тело issue #196, раздел «Критерии приёмки», AC2: «код tooltip
|
||||
вызывает общий `_roomHum()`, а не дублирует resolver».
|
||||
|
||||
Первая половина AC2 (значение override совпадает с настроенным
|
||||
`hum_source`) действительно проверяется браузерным смоком через видимый
|
||||
текст. Вторая половина («не дублирует resolver») — по природе утверждение о
|
||||
структуре кода, а не о видимом поведении; смок не может отличить «вызвал
|
||||
`_roomHum()`» от «скопировал ту же формулу в другом месте с тем же
|
||||
результатом». Это не отменяет проверяемость: AC5 уже требует ревью кода на
|
||||
отсутствие новых resolver'ов/scope creep, так что содержательно вторая
|
||||
половина AC2 покрыта — реального пробела в доказательстве нет, только
|
||||
неточная маркировка способа доказательства одним словом `smoke` на весь
|
||||
пункт.
|
||||
|
||||
**Обоснование severity:** Low — не блокирует, ничего не остаётся
|
||||
непроверенным, дефект чисто редакционный (смешаны behavioral и structural
|
||||
части одного AC под одной меткой доказательства).
|
||||
|
||||
**Решение ревьюера:** снимаю без возврата ТЗ на правку. Код-ревью (AC5)
|
||||
обязан явно подтвердить, что `_roomHum()` вызывается напрямую, без
|
||||
дублирования резолвера — этот пункт настоящим решением фиксируется как
|
||||
входящий в объём AC5, а не отдельно требующий правки текста ТЗ.
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- **Диагноз проблемы не голословен.** `_roomHum()` (`:16777-16781`)
|
||||
дословно симметричен `_roomTemp()` (`:16769-16774`), уже используется в
|
||||
room label (`:17085-17086`) и уже настраивается в диалоге комнаты
|
||||
(`_renderRoomSource('hum')`, `:19717-19838`) — но не передаётся в
|
||||
`_showTip()` при hover (`:15627-15633`). Пробел реален, не выдуман.
|
||||
- **Соответствие `docs/SCOPE.md`:** закрывает J5, персона home
|
||||
admin/household member, поверхность View; не расширяет lock invariant,
|
||||
out-of-scope список, backend/schema/migration.
|
||||
- **Контракт защищает device tooltip от регрессии** (п.1, подтверждено
|
||||
реальным call site `:16729-16731`, который не передаёт `lqi`/`temp`
|
||||
вовсе — пятый необязательный параметр `hum` для него ничего не меняет).
|
||||
- **Touch-поверхность не ухудшается.** Room label уже показывает влажность
|
||||
без hover (`disp.labelHum`, `:17084-17086`); hover-only статус самого
|
||||
тултипа — существующее, уже задокументированное ограничение
|
||||
(`USER-GUIDE.ru.md:1310`), не создаваемое этим ТЗ. Правило `Touch editor:
|
||||
…` из `docs/TOUCH-SUPPORT.md` не применяется — это не editor-фича.
|
||||
- **AC1-AC5 однозначны, каждый снабжён допустимым способом доказательства**
|
||||
(`smoke`/`unit+build`/«ревью кода»); формат значения (`Math.round`,
|
||||
целое число, суффикс `%`) подтверждён на уровне `devices.ts` — в тултипе
|
||||
не появится дробное число, которое пришлось бы отдельно форматировать.
|
||||
- **i18n-ключ следует существующей конвенции** (`tip.temp_avg` →
|
||||
`tip.hum_avg`, тот же шаблон текста на обоих языках).
|
||||
- **AC3 корректно защищает границы:** `null`/невалидный источник не
|
||||
показывает строку (тот же паттерн `!= null`, что уже используют
|
||||
`tip.temp`/`tip.lqi`), device tooltip не получает ложную влажность,
|
||||
`_noHover` не создаёт тултип — все три случая проверены по коду и
|
||||
логически вытекают из уже существующих guard'ов, не из нового кода,
|
||||
который ещё предстоит написать правильно.
|
||||
- **Откат тривиален** — один user-visible коммит, без миграции схемы/данных,
|
||||
без cleanup.
|
||||
- **Принятые технические предположения (3 пункта)** — все технические
|
||||
(порядок параметра `_showTip`, отсутствие пересчёта формата, свобода
|
||||
имён тестов), продуктовый вопрос под видом технического решения не
|
||||
найден.
|
||||
- **Не додумано скрытых предположений о поведении.** Ни одно утверждение
|
||||
контракта не описывает поведение, которого нет в коде/канонических
|
||||
документах и не помечено как assumption.
|
||||
|
||||
## Чего не проверял
|
||||
|
||||
- Реализацию — её нет: ветка `issue/196-*` в репозитории отсутствует,
|
||||
продуктовый код не менялся (`git branch -r` не находит ветку).
|
||||
- Гейты `typecheck`/`test`/`build`/smoke — не относятся к этапу ревью ТЗ
|
||||
(PROCESS.md §2.4/§8), выполнить их не над чем.
|
||||
- `python -m pytest tests_backend` — задача не касается backend ни кодом,
|
||||
ни текстом ТЗ.
|
||||
- Реальный визуальный результат (браузерный рендер строки влажности,
|
||||
точное позиционирование `.tip`) — CSS/разметка ещё не написаны;
|
||||
заявленное («тот же паттерн, что и temp/lqi») — это план, а не
|
||||
исполненный факт, что и должно быть на этапе ТЗ.
|
||||
- Точность числовых оценок аналитики (4/10, 2/10, P2) по существу — поле
|
||||
владельца (PROCESS.md §2.2), не предмет ревью ТЗ.
|
||||
- Возможность технического спора автор/ревьюер по разделу «Принятые
|
||||
технические предположения» — не возникла, все три пункта разумны без
|
||||
необходимости их оспаривать.
|
||||
|
||||
## Вердикт
|
||||
|
||||
Зелёный. High: 0, Medium: 0, Low: 1 (AC2 смешивает поведенческую и
|
||||
структурную часть под одной меткой `smoke` — не оставляет пробела в
|
||||
доказательстве, так как структурная часть уже покрыта AC5; снимается без
|
||||
возврата ТЗ на правку, код-ревью проверяет обе половины). ТЗ подтверждено
|
||||
построчным чтением кода `origin/dev`: каждое техническое утверждение
|
||||
оказалось фактом (с точностью до нормального смещения номеров строк из-за
|
||||
коммитов после написания ТЗ), продуктовых вопросов владельцу нет и не
|
||||
додумано новых, AC1-AC5 однозначны и снабжены допустимыми способами
|
||||
доказательства, откат тривиален, лёгкий трек применён корректно (новый
|
||||
i18n-ключ и enhancement-тип верно исключают `trivial`, сложность/риск ≤3 и
|
||||
одна поверхность верно подтверждают `small`).
|
||||
|
||||
**Вердикт: зелёный · цикл r1/2 · High: 0 · Medium: 0 → в задаче · Документ:
|
||||
docs/reviews/SPEC-REVIEW-196-r1.md**
|
||||
Reference in New Issue
Block a user