docs: review document for #358

Issue: #358
User-Visible: no
This commit is contained in:
claude[bot]
2026-08-28 18:39:51 +00:00
parent 61911b8656
commit 5a662d829d
+156
View File
@@ -0,0 +1,156 @@
# 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 проверяем и снабжён способом доказательства,
фактические утверждения о коде подтверждены построчно, догадок, выданных за
решения, не найдено. Готово к разработке.