mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-29 03:09:36 +00:00
Ревью #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
93 lines
18 KiB
Markdown
93 lines
18 KiB
Markdown
# 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
|