From 03aaad3e2b47afc92c08e21f90adc8746344d6cd Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:54:38 +0000 Subject: [PATCH] docs: review document for #588 Issue: #588 User-Visible: no --- docs/reviews/SPEC-REVIEW-588-r3.md | 92 ++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 docs/reviews/SPEC-REVIEW-588-r3.md diff --git a/docs/reviews/SPEC-REVIEW-588-r3.md b/docs/reviews/SPEC-REVIEW-588-r3.md new file mode 100644 index 00000000..22c1a3b3 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-588-r3.md @@ -0,0 +1,92 @@ +# SPEC-REVIEW-588-r3 — «Настройки устройства: отображение „Значение + статичный значок“» + +Issue: [#588](https://github.com/Matysh/houseplan-card/issues/588) +Этап: spec (полный трек — S2-analysis назвал явно два нарушенных критерия §5: «одна поверхность» и «нет нового UX-контракта») +Заход: r3 · блокирующих циклов израсходовано на входе 2/4 (зелёный вердикт этого раунда бюджет не тратит, #227) + +## Вердикт + +**Зелёный.** High: 0. Medium: 0. Low: 0. + +## Скоуп разбора (по дельте, §2.10) + +Предыдущий вердикт: жёлтый, заход r2, [docs/reviews/SPEC-REVIEW-588-r2.md](../../docs/reviews/SPEC-REVIEW-588-r2.md), материал — тело issue на момент разбора (SHA-256 тела `40a3c8b12af7fe3803ad3fece1a00b54c8fa12bd8fdc36b6bccd6516bf41756f`, дерево материала `002d320431837447efa84ee6119a95334bac4080`, рабочая копия кода на `git rev-parse HEAD` = `5b7add94d15a3961adc7f288f00750820cbbba07`). + +Дельта между r2 и r3 объявлена автором в комментарии перехода (`Matysh`, 2026-09-18T08:48:00Z) и подтверждена построчной сверкой текущего тела issue с цитатами в документе r2: + +1. **AC6** переписан: три факта (`.vacpuck` отсутствует, `.vactrail` отсутствует, `.vacwarn` отсутствует) разведены по **двум** конфигурациям — puck/след доказываются сопоставленной картой (образец `:86-180`), `.vacwarn` — только несопоставленной (`map_name: 'm9'`, рецепт `demo/smoke_vacuum_multifloor.mjs:70-78`, `unmappedWarns`); явно объяснено, почему совмещённая фикстура делала третий факт тавтологией (маршрут `ready` ⇒ `routeWarningKey` всегда `null`); добавлено требование доказывать обе стороны в каждой конфигурации (при `value_static_icon` факта нет, при `badge` на том же маркере он есть). +2. **Риск №1** дополнен фразой о причине недоказуемости третьей ветки на исходной фикстуре и о том, что новый блок попутно закрывает унаследованный пробел покрытия (`.vacwarn` не был доказан автотестом и для `static_icon`). +3. **План автотестов** обновлён: `demo/smoke_static_icon.mjs` описан как два блока конфигурации (сопоставленная + несопоставленная карта), каждый факт с обратным переключением в `badge`. + +Дельта локальна: новая подсистема не затронута (пылесос уже был в скоупе r1/r2), контракт К1, К2, К3–К6, AC1–AC5, AC7–AC12, скоуп/не-скоуп, i18n, модель данных, откат, release-артефакты не менялись. Код в рабочей копии не изменился между r2 и r3 (фича не реализована, HEAD сейчас `eaaba7ab42914feeed3f6fda05f965c4515f03b7` — это только документы ревью r1/r2, коммитнутые пайплайном; продуктовый код тот же, что проверялся в r2). По §2.10 повторно проверялись только AC6, риск №1 и план автотестов — плюс код, на который они ссылаются. Остальное унаследовано без повторной проверки (раздел ниже). + +## Закрытие раунда r2 + +| Находка r2 | Чем закрыта | Где это видно | +|---|---|---| +| **M3** (в скоупе). Третий факт AC6 (отсутствие `.vacwarn`) доказывался образцом `:86-180`, где карта откалибрована и совпадает с `map_name` — маршрут `ready`, `routeWarningKey` для `ready` всегда `null`, поэтому бейдж не появляется ни в одном режиме и мутация ветки `_vacRouteBadge` прошла бы зелёной и на дефектном коде. | AC6 явно разведён на две конфигурации: puck/след — сопоставленная карта (тот же образец `:86-180`), `.vacwarn` — несопоставленная (`map_name: 'm9'`, рецепт `unmappedWarns`); причина разведения записана в самом критерии, чтобы не свернуть обратно к одной фикстуре; требование доказывать обе стороны (факт есть при `badge`, нет при `value_static_icon`) исключает повтор той же ошибки для новой конфигурации. Риск №1 фиксирует и то, что это попутно закрывает унаследованный пробел покрытия у `static_icon`. | Тело issue, таблица AC (строка AC6), раздел «Риски» (пункт 1, последнее предложение), «План автотестов» (строка `demo/smoke_static_icon.mjs`). Код-факты подтверждены повторным чтением: `src/vacuum-routes.ts:344-353` (`routeWarningKey`) возвращает не-`null` только для `unmapped`/`needs_calibration`/`ambiguous`/`missing_space`; `demo/smoke_vacuum_multifloor.mjs:70-85` действительно создаёт несопоставленную карту (`m9`) под движущимся роботом и получает `unmappedWarns` на маркере без `display` (т.е. на дефолтном `badge`); `demo/smoke_static_icon.mjs:86-180` (образец) задаёт `calibration: { m1: [...] }` при `map_name: 'm1'` — совпадение, маршрут `ready`, что и требовалось для puck/след, но не для `.vacwarn`. | + +## Унаследовано из r2 + +Без повторной проверки в этом раунде — код и текст этих разделов не менялись дельтой r2→r3; см. [SPEC-REVIEW-588-r2.md](../../docs/reviews/SPEC-REVIEW-588-r2.md) (материал: тело issue SHA-256 `40a3c8b1...b7e6ce`, дерево `002d3204...`, рабочая копия `5b7add94d15a`) и через него [SPEC-REVIEW-588-r1.md](../../docs/reviews/SPEC-REVIEW-588-r1.md) (материал: тело issue SHA-256 `ee15ac6d...b7e6ce`, дерево `14bf5e35f9bc...`, `dev`@`a6185e295d2e`): + +- Сценарий и «что человек увидит до/после» — форма и содержание по §7.1 (r1 §«Что проверено и корректно», п.1). +- Скоуп/не-скоуп — соответствует файлам, реально содержащим логику `static_icon`/`value`; не-скоуп корректно исключает `import_export.py`; фраза про «подсветку убираемой комнаты» убрана (M2, закрыт в r2), #589 существует, открыт, метки `bug, docs, vacuum, P3, S1-new` — проверено повторно в r2, не проверялось заново здесь. +- Контракт К1, К3–К6 и К2а (кроме переписанного AC6) — подтверждён построчным чтением кода в r1/r2 (быстрый путь `sourceDetails`, четыре причины отказа значения, подавление пульсации/бейджа у `static_icon`, порядок `DISPLAY_MODES`, видимость поля «Источник значения», три литеральных сравнения режима в `houseplan-card.ts:12198,12414,12593`). +- Модель данных, миграция, деградация старого клиента (`normalizeDeviceDisplay` → `badge`) — проверено в r1. +- i18n-ключи и их именование — не менялись. +- AC1–AC5, AC7–AC12 — содержательно не изменились со r2; номера и содержание сверены r1/r2 построчно. +- Дубликаты (#3, #26, #158, #219) — принято на слово в r1, дельта их не касается. +- «Обязательные разделы §7.1 — комплектность» — весь список присутствует (сценарий, «до/после», проблема, скоуп/не-скоуп, контракт поведения, UX, модель данных и миграция, i18n, AC1…AC12 с доказательством, план автотестов, риски, откат, release-артефакты); r1 подтвердил один раз, дельта не убрала ни одного раздела. +- Терминология «Отображение» (`docs/USER-GUIDE.ru.md:1280-1287`) — совпадает с ТЗ; не проверялась заново, так как раздел USER-GUIDE дельтой не затронут (сверено повторно только фактом наличия строк в файле, см. «Как проверялось»). + +## Как проверялось (только по дельте) + +1. Построчно сравнил текущее тело issue с цитатами AC6/риска №1/плана автотестов в документе r2 и с описанием автора в комментарии перехода (`08:48:00Z`) — дельта соответствует объявленной, скрытых расхождений не нашёл. +2. Перечитал `src/vacuum-routes.ts:320-353` (`planVacuumOverlay`, `routeWarningKey`) — подтвердил, что `routeWarningKey` возвращает не-`null` только для `unmapped`/`needs_calibration`/`ambiguous`/`missing_space`, для `ready` (и любого другого `kind`) — `null`. Точное текстовое основание claim'а AC6. +3. Перечитал `demo/smoke_static_icon.mjs:86-180` (образец для puck/след) — калибровка `m1` совпадает с `map_name: 'm1'` камеры ⇒ `ready`; корректный образец для двух первых фактов, бесполезен для третьего — ровно как теперь и написано в AC6. +4. Перечитал `demo/smoke_vacuum_multifloor.mjs:50-99` — подтвердил рецепт `unmappedWarns` (`map_name: 'm9'` под движущимся роботом, строки 76-82) и что маркер `e_vacuum_robo` не задаёт `display` явно (значит дефолт `badge` из `DISPLAY_MODES[0]`, `src/logic.ts:898`) — то есть сравнение «при `badge` факт есть» в AC6 опирается на реальный существующий тест, а не на предположение. +5. Перечитал три ветки пылесоса в `src/houseplan-card.ts` (`_vacTick:12194-12201`, рендер puck/trail:12405-12497, `_vacRouteBadge:12591-12599`) — код не изменился со r2 (фича не реализована), все три по-прежнему сравнивают `normalizeDeviceDisplay(...) === 'static_icon'` напрямую; буфер `_vacRt` наполняется в `_vacTick` независимо от результата калибровки маршрута (зависит только от `moving` и телеметрии камеры) — значит четвёртый факт AC6 («буфер не наполняется») проверяется той же веткой кода (`:12198`) и естественно доказывается в любой из двух конфигураций AC6, отдельной третьей конфигурации под него не требуется — неоднозначности здесь нет. +6. Проверил нумерацию AC1…AC12 в текущем теле issue — 12 строк, без дублей и пропусков, соответствует таблице; «План автотестов» и «Классы риска» ссылаются на те же номера без рассинхронизации. +7. Проверил issue #589 повторно (`gh issue view 589`) — открыт, метки `bug, P3, docs, vacuum, S1-new` не изменились, всё ещё корректны. +8. Гейты кода не запускал — на этапе ревью ТЗ неприменимо: фича не реализована, продуктовый код между r2 и r3 не менялся (диффу подвергалось только тело issue). + +## Находки + +Не найдено. M3 закрыт полностью и без остатка: новая формулировка AC6 технически точна (подтверждено построчным чтением `vacuum-routes.ts` и обоих демо-файлов), устраняет тавтологию третьего факта и попутно документирует унаследованный пробел покрытия у `static_icon`, не открывая новых пробелов (проверено, в т.ч. для четвёртого факта — буфера `_vacRt`, который в исходной формулировке AC6 не был явно привязан к одной из двух конфигураций, но по коду доказуем в любой из них). + +## Что проверено и корректно + +- AC6 в новой редакции доказывает все четыре заявленных факта (puck, след, `.vacwarn`, буфер) существующими в проекте фикстурами/рецептами без тавтологии; мутант `value-static-icon-keeps-live-vacuum` способен покраснеть на каждой из трёх строковых веток, включая `_vacRouteBadge`, при указанной несопоставленной конфигурации. +- Риск №1 корректно фиксирует причину недоказуемости прежней формулировки и явно называет её унаследованной (не только новой) проблемой покрытия. +- Обратное переключение в `badge` в обеих конфигурациях AC6 опирается на реально существующий признак различия (`badge` — дефолтный режим без подавления пульсации/бейджа/цвета), а не на предположение. +- Нумерация и перекрёстные ссылки AC/«План автотестов»/«Классы риска» непротиворечивы после правки. +- Всё унаследованное из r1/r2 (см. раздел выше) не тронуто дельтой — не проверялось повторно, обоснование дано. + +## Чего не проверял + +- Не проверял повторно AC1–AC5, AC7–AC12, контракт К1/К3–К6, скоуп/не-скоуп, модель данных, i18n, дубликаты (#3/#26/#158/#219) — дельта r2→r3 их не касается, r1/r2 уже проверили (см. «Унаследовано из r2»). +- Не читал `custom_components/houseplan/validation.py` и `scripts/config-schema.json` — как и в r1/r2, это предмет код-ревью. +- Не запускал `tsc`/`npm test`/`npm run build`/`npm run golden:verify` — на этапе ревью ТЗ неприменимо: фича не реализована, диффу подвергалось только тело issue, а не код. +- Не проверял, выполнено ли исправление #589 — не входит в скоуп #588 и не блокирует эту задачу. +- Не перепроверял содержимое `docs/USER-GUIDE.ru.md` целиком — только строки таблицы «Четыре варианта „Отображение“», релевантные терминологии ТЗ (не менялись дельтой). + +## Материал раунда + +- Issue: #588, тело на момент разбора r3 (SHA-256 нормализованного тела — конвейер впишет в блок якорей публикации ниже). +- Заход r3, циклов ревью ТЗ израсходовано на входе 2 из 4 (лимит для полного трека — 4). Этот вердикт зелёный и бюджет не тратит (§4, #227) — при принятии в разработку счётчик остаётся 2/4. +- Рабочая копия репозитория на момент проверки кодовых фактов: `git rev-parse HEAD` = `eaaba7ab42914feeed3f6fda05f965c4515f03b7`; продуктовый код (`src/**`, `custom_components/**`) идентичен коду, проверенному в r2 (`5b7add94d15a`) — фича не реализована, между раундами менялось только тело issue и коммитились документы ревью. + +--- + + + +## Материал раунда + +- Ветка: `dev`, коммит `eaaba7ab4291` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `2cd69c616ddd3f719677420519d22b6da3e9a38b` + ``` + git log --all --format='%H %T' | grep 2cd69c616ddd + ``` +- Тело issue: `503d9f130d95a9bb723107478993f63ddb64e51e20b212e60a0f9d8dac331e44` +- Вердикт конвейера: `green` · High 0