Files
houseplan-card/legacy/reviews/v1.77.0/SPEC-REVIEW-588-r3.md
T
Claudeandclaude[bot] 0991c45374 fix(tools): архив переписывает относительные ссылки перенесённых документов (#682)
Ревью #682 r1, Medium: перенос добавляет документу уровень вложенности
(`docs/reviews/X.md` → `legacy/reviews/<тег>/X.md`, `docs/specs/` →
`legacy/specs/`), а относительные ссылки внутри перенесённых документов и в
соседях, ссылавшихся на них, никто не пересчитывал — на `97d19268` 53 битые
ссылки в 46 файлах (заявление «все 26 резолвятся» в `7feb6177` было верно
только до переноса документов ревью). Гейты архив не смотрят.

`reviews-archive.mjs`: `repairLinks` пересчитывает ссылку, если она не
резолвится от нового места, а цель находится от нового или старого места
через карту переносов; битая и до переноса ссылка не трогается. `--apply`
делает это само, `--repair-links=<rev>` — для всех переименований
`<rev>..HEAD`, `--check-links` печатает битые. Этим коммитом
`--repair-links=origin/dev` переписал ровно 53 ссылки в 46 файлах; остались
две прежние «...»-заглушки в CODE-REVIEW-448-r2 (битые и на dev). Тесты:
перенесённый документ, сосед со ссылкой в архив, ТЗ со ссылкой на позже
перенесённое ревью, битая-до-переноса не трогается, в `legacy/` битых нет;
мутант `reviews-archive-links-from-new-place-only`. PROCESS §2.10 и
DEVELOPMENT › Release называют переписывание и `--check-links`.

Issue: #682
User-Visible: no
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
2026-09-27 22:10:47 +00:00

93 lines
18 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-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](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](SPEC-REVIEW-588-r2.md) (материал: тело issue SHA-256 `40a3c8b1...b7e6ce`, дерево `002d3204...`, рабочая копия `5b7add94d15a`) и через него [SPEC-REVIEW-588-r1.md](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 и коммитились документы ревью.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `dev`, коммит `eaaba7ab4291` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `2cd69c616ddd3f719677420519d22b6da3e9a38b`
```
git log --all --format='%H %T' | grep 2cd69c616ddd
```
- Тело issue: `503d9f130d95a9bb723107478993f63ddb64e51e20b212e60a0f9d8dac331e44`
- Вердикт конвейера: `green` · High 0