Files
houseplan-card/docs/reviews/SPEC-REVIEW-358-r1.md
T
2026-08-28 18:39:51 +00:00

157 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-358-r1
**Issue:** #358 — «Холодная вкладка + пылесос (реальная телеметрия): `_vacMapId`
бросает в `willUpdate` — карточка перестаёт обновляться»
**Этап:** spec (PROCESS.md §2.4) · **Трек:** small (§5, метка `small`,
подтверждено владельцем: комментарий S2→S3 от 2026-08-28) · **Заход:** r1 ·
блокирующих циклов израсходовано 0 из 2.
## Скоуп
Лёгкий трек: ТЗ живёт в теле issue #358 (раздел «## ТЗ (small-трек, S3)»),
файла в `docs/specs/` нет — соответствует §5, проверено `ls docs/specs/`.
ТЗ описывает три пункта контракта:
- **К1** — перенос реализации `_vacMapId` с `houseplan-editor-runtime.ts` на
`houseplan-card.ts` (владелец полей — карта), runtime делегирует в host. Тот
же приём, что #357 применил к `_toggleIntent`.
- **К2** — два защитных гарда: `_decorShapeDown` (парный гард к
`_decorShapeDbl` из #337) и рендер диалога `_vacCalConfirm` за
`this._editorRuntime ?`.
- **К3** — новый холодный смок `demo/smoke_cold_view_vacuum.mjs`, закрывающий
слепое пятно демо-фикстуры (`vacuum.mower` без атрибутов позиции).
AC1–AC4 повторяют issue-AC1…4 с указанием способа доказательства (smoke /
smoke + мутант / smoke + чтение кода). Откат — один revert, без миграций.
## Как проверялось
Ревью ТЗ этапа `spec` не гоняет гейты (§8 относится к код-ревью) — предмет
проверки этого этапа: выполнимость и однозначность ТЗ, и то, что автор не
выдал догадку за факт. Проверка велась чтением действующего исходника на
`61911b86` (текущий `HEAD`, тот же SHA, на котором зелёный CI Validate уже
подтверждён — #343) и сопоставлением каждого утверждения ТЗ с кодом.
Прочитано и сверено:
1. `docs/SCOPE.md` — сценарий issue закрывает J1 («живой обзор дома»): баг
останавливает весь цикл обновления Lit, то есть бьёт по ядру продукта
(кухня/дача с киоском), а не по периферийной функции.
2. `PROCESS.md` §2.4, §5, §7.1 — обязательные разделы лёгкого трека
(проблема · контракт · AC с доказательством · откат) присутствуют.
3. Тело issue #358 целиком + единственный комментарий (владелец, S2→S3).
4. `docs/VACUUM.md` — цепочка фолбэка `map_name → current_map → map_index →
selected_map → default` совпадает с описанием К1.
5. Код:
- `src/houseplan-card.ts:11707-11709` — сегодняшняя жёсткая заглушка
`_vacMapId`, вызываемая без гарда из `_captureRenderDeviceSnapshot`
(`:4471`, внутри `willUpdate`) и `_renderVacuums` (`:11807`) — подтверждён
ровно тот баг, который описывает issue;
- `src/houseplan-editor-runtime.ts:9979-9985` — реализация, которую К1
предлагает перенести. Текст в ТЗ (`const sel = ve ? planHass?.states?.[ve]
?.attributes?.selected_map : null; return vacMapIdWithFallback(tele.mapId,
sel);`) и комментарий HP-1541-01 процитированы буквально, без искажений;
- `src/vacuum.ts:252` — `vacMapIdWithFallback` действительно экспортируется
оттуда и реализует «не-nullish» правило, о котором говорит комментарий;
- `src/houseplan-editor-runtime.ts:11633-11648` — прецедент того же приёма
на `_toggleIntent` из #357: карта владеет реализацией, runtime держит
делегирующего двойника (`return this.host._toggleIntent(...)`), внутренние
вызовы runtime (`this._toggleIntent(...)` на `:11929`, `:11648`) продолжают
работать без изменений. К1 в точности повторяет эту схему — не догадка, а
проверенный шаблон;
- `src/houseplan-card.ts:7630-7632` — `_decorShapeDown` действительно жёсткая
заглушка без гарда; `:7639-7642` — `_decorShapeDbl` действительно уже
несёт `if (!this._editorRuntime) return;` (гард из #337, на который К2
ссылается как на образец);
- `src/styles/plan.styles.ts:664` — `.decorlayer .dshape { pointer-events:
none; }` вне `mode-decor` подтверждает, что реальный указатель не достаёт
фигуру в View, а значит только синтетический `pointerdown` (как в плане
смока К3-г) может воспроизвести падение — соответствует помеченной в
issue пометке «проверено» (исполнением, не гипотеза);
- `src/houseplan-card.ts:11346-11361` — ряд редакторских диалогов
действительно рендерится по шаблону `${this._X ? this._editorRuntime ?
html\`…\` : nothing : nothing}`, а `_vacCalConfirm` (`:11351`) — единственный
из них без внешнего гарда `this._editorRuntime ?`. К2 описывает точное
целевое выражение, совпадающее с этим шаблоном;
- `src/houseplan-editor-runtime.ts:10038,10052` — `_vacCalConfirm`
присваивается truthy-значение только внутри runtime; в
`houseplan-card.ts` встречается лишь сброс в `null`. Значит утверждение
AC4 «кейс недостижим исполнением по построению» — не догадка, а факт,
подтверждённый полным grep всех присваиваний;
- `src/houseplan-card.ts:11730-11731` — `_vacApplyCalibrationProposal`
(кнопки диалога) — тоже жёсткая заглушка, как и указано в issue.
6. `demo/smoke_vacuum.mjs`, `demo/smoke_cold_view_toggle.mjs`,
`demo/serve.mjs` (`launchColdView`) — механика инъекции сущности с
телеметрией через `card.hass`/`card._serverCfg`/`card._layout` и трекинг
`page.on('request'/'pageerror')` уже существует и покрывает ровно то, что
требует план К3 (несколько кадров `hass`, отсутствие запроса
`houseplan-editor-runtime-*.js`, ноль pageerror, синтетический pointerdown).
Смок, описанный в К3, технически реализуем без новой инфраструктуры.
7. `scripts/mutation-gate.mjs` — реестр мутаций, шаблон записи
`cold-view-toggle-delegated-to-runtime` (добавлен в #357) — ревертит
`_toggleIntent` карты назад на делегацию в runtime и проверяется тем же
холодным смоком. AC3 просит зеркальную запись для `_vacMapId`; формулировка
ТЗ («вернуть делегацию `_vacMapId` в runtime») однозначно отображается на
эту механику — проверяемый, а не расплывчатый критерий.
8. `demo/smoke_static_icon.mjs`, `demo/smoke_vacuum_firstuse.mjs` — подтверждён
слепое пятно демо-фикстуры: `vacuum.mower` заведён без `vacuum_position`,
значит ни один существующий смок (включая холодные) не проходит через
ветку `telemetry ? this._vacMapId(...) : null`.
## Находки
Нет. High: 0, Medium: 0, Low: 0.
Отдельно проверено на «догадку, выданную за факт» (главный риск по инструкции
ревью): каждое утверждение К1/К2/К3/AC1-4, сформулированное как факт о текущем
коде, сверено с исходником построчно (см. таблицу выше) и подтвердилось без
исключений. Технические решения, оставленные без явного обсуждения (имя файла
смока, точный id мутанта, порядок гардов), относятся к классу «агенты решают
сами» (§7.1) и не требуют пометки «принято предположительно» — они не
затрагивают видимое пользователю поведение.
## Что проверено и корректно
- Соответствие лёгкому треку (§5): ТЗ в теле issue, файла в `docs/specs/` нет.
- Обязательные разделы ТЗ лёгкого трека присутствуют и однозначны.
- Каждый AC называет способ доказательства (smoke / smoke+мутант /
smoke+чтение кода) и не содержит открытых вопросов.
- К1 текстуально и по логике совпадает с действующим кодом-источником
(`houseplan-editor-runtime.ts`) и с прецедентом #357.
- К2 описывает ровно два реальных гэпа (найдены построчным grep всех
присваиваний/рендеров) и предлагает решение по образцу уже принятого кода.
- К3 реализуем на существующей инфраструктуре демо-стенда без новых
примитивов.
- Продуктовая рамка (сценарий, кого касается, почему это выше по тяжести, чем
#357) присутствует в теле issue и совпадает с J1 из `docs/SCOPE.md`.
- Открытых продуктовых вопросов к владельцу нет — и по факту не должно быть:
ожидаемое поведение («карточка живёт на холодной вкладке») уже
зафиксировано как баг, а не как решение с вариантами.
- Откат описан («один revert, конфиг/контракты не меняются») и достаточен для
этого класса правки (без миграций, без нового UX-контракта).
## Чего не проверял
- Гейты `npx tsc --noEmit` / `npm test` / `npm run build` не гонялись: на
этапе `spec` кода ещё нет (issue не покидал `S3/S4`), гонять их не над чем.
Дешёвые гейты на `61911b86` (последнем коммите `dev`) уже зелёные —
Validate: https://github.com/Matysh/houseplan-card/actions/runs/33198366583.
- Исполнение самого смока `demo/smoke_cold_view_vacuum.mjs` — он ещё не
написан, это предмет реализации, не ТЗ. Оценивалась только его
осуществимость на существующей инфраструктуре (`launchColdView`, паттерны
инъекции сущности), а не его будущий текст.
- `scripts/smoke-links.mjs` / реестр обнаружения смоков — не проверял,
требует ли новый файл смока отдельной регистрации там; это техническая
деталь реализации, а не предмет ТЗ, и её увидит код-ревью.
- Golden/визуальные эталоны, backend-тесты (`tests_backend`),
performance-профили — вне скоупа: правка не трогает `custom_components/**`,
не меняет визуальный результат и не названа в AC как влияющая на
производительность.
## Вердикт
Зелёный. ТЗ выполнимо, каждый AC проверяем и снабжён способом доказательства,
фактические утверждения о коде подтверждены построчно, догадок, выданных за
решения, не найдено. Готово к разработке.