21 KiB
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).
Как проверялось
- Прочитаны целиком
docs/SCOPE.md,AGENTS.md,PROCESS.md(действующая редакция, включая §1, §2.4, §5, §7.1, §8). - Прочитано тело issue #196 целиком и все три комментария: аналитика
владельца (ценность 4/10 · сложность 2/10 · P2 ·
enhancement· поверхностиsrc/houseplan-card.ts+ i18n + smoke/docs · трекsmall,trivialявно отклонён из-за нового i18n-ключа) и два «Взял:» на роли аналитика/автора ТЗ. - Построчно сверены все технические утверждения раздела «Сейчас» и
«Контракт» с кодом
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-3temp_source/hum_source→ иначе_climate().get(area)»)._roomHum()уже существует и уже используется в room label (:17085-17086,${hm}%— тот же формат значения, что просит ТЗ). Ссылка на реальный существующий helper, а не на функцию, которую ещё нужно написать._climate()—houseplan-card.ts:16792-16802: один кеш наhassidentity, комментарий на: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}%без округления в тултипе не потеряет точность.
- Прочитан
src/i18n/en.jsonиru.json:tip.temp_avg(:374"average temperature:"/"средняя температура:") иtip.lqi(:150) — предложенныйtip.hum_avg("average humidity:"/"средняя влажность:") следует тому же формату (двоеточие, строчная буква,average <metric>:), новый ключ не выдуман по структуре. - Прочитан
docs/USER-GUIDE.ru.md: строка 173 (таблица режимов) — «подсказка показывает название, чистую площадь, температуру и LQI» — это ровно то место, которое ТЗ обязано обновить (и заявляет это в разделе Release), и ровно то место, что фиксирует нынешний пробел (влажности в перечислении нет, хотя строка 128/275/1175 подтверждает, что влажность уже показывается в room card). Строка 1310 «Комнатные подсказки основаны на hover» — существующее, не создаваемое этим ТЗ ограничение, распространяется на новую строку тем же образом, что и на текущие. - Прочитан
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. - Проверено, что
docs/specs/196-*.mdне существует (ls docs/specs/— пусто по маске) — соответствует лёгкому треку. - Проверено соответствие
docs/SCOPE.md: задача закрывает J5 «Room climate at a glance» (persona home admin/household, View), не расширяет out-of-scope списки, не касается lock invariant, не создаёт нового job'а. - Проверены оба существующих файла-кандидата для 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