docs: review document for #162
Проверка (CI) / Классификация изменённых файлов (push) Successful in 33s
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 47s
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 3m39s

Issue: #162
User-Visible: no
This commit is contained in:
claude[bot]
2026-09-03 17:10:17 +00:00
parent 49f69f60db
commit 53f91d974a
+177
View File
@@ -0,0 +1,177 @@
# Код-ревью #162 · заход r2
Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0
## Скоуп раунда
Предыдущий код-ревью (r1) прошёл на HEAD `4032b810` по тексту вердикта, но
фактически ревьюемое дерево включало и `8e2892b9` (CI-фиксы, запушены
2026-09-03 19:42, до публикации вердикта в 19:47) — документ `fe18f80f`
(`docs: review document for #162`) физически является потомком `8e2892b9`
(`git log --format='%h %p' -1 fe18f80f` → `fe18f80f 8e2892b9`). Это
несоответствие SHA в тексте вердикта самому дереву — сам факт не критичен
(правки внутри 8e2892b9 не оспаривались), но найден только чтением git-графа,
а не заявлен явно. Базой для дельты этого раунда взят `8e2892b9`, а не
заявленный `4032b810`, как единственный SHA, действительно совпадающий с
деревом, на котором был вынесен вердикт.
Разбор — по дельте, а не заново: `git diff 8e2892b9..HEAD` (HEAD = `49f69f60`),
две правки-коммита автора (`4b8cf6e0`, `49f69f60`) плюс автоматический
коммит публикации ревью (`fe18f80f`, не код). Дельта закрывает ровно две
находки r1 и не трогает ничего вне их: резолвер маршрутов, серверный
recorder, валидацию config/set, space-deletion, экспорт — не менялись.
## Как проверялось
- Прочитаны оба изменённых участка кода целиком:
`src/houseplan-editor-runtime.ts` (`_vacAutoCalibrate`, `_vacApplyCalibrationProposal`),
`src/editors/vacuum-maps-section.ts` (новый блок «Добавить источник карты»).
- Прослежена цепочка `calibrationTarget()` (`src/vacuum-route-edit.ts:153-163`,
не менялась в этой дельте) → `_vacAutoCalibrate` → `_vacCalConfirm.space` →
`_vacApplyCalibrationProposal` — вручную сверено, что `target.space` есть
`route?.space || dockSpace`, то есть пространство маршрута, а не дока.
- Прогнаны гейты лично (см. раздел «Гейты»), включая точечный прогон нового
мутанта и восьми смоков с прямым совпадением символов дельты.
## Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
| **High** — `_vacApplyCalibrationProposal` коммитил `dev.space` (пространство дока) вместо пространства маршрута при ручной донастройке после high-residual auto-calibration; AC8 нарушался в штатной ветке | `_vacCalConfirm` получил поле `space?: string`; `_vacAutoCalibrate` кладёт в него `target.space` (`src/houseplan-editor-runtime.ts:11035`); `_vacApplyCalibrationProposal` коммитит `proposal.space \|\| dev.space` вместо голого `dev.space` (`src/houseplan-editor-runtime.ts:11055-11056`) | Код: `houseplan-editor-runtime.ts:11029-11035` (запись `space: target.space`) и `:11054-11058` (чтение с фолбэком). Доказательство: `demo/smoke_vacuum_multifloor.mjs:87-104` — новый сценарий «док на f1, маршрут vr2/m2 на garden, высокий residual → ручная подгонка» проверяет `_space === 'garden'` и `_vacFit.routeId === 'vr2'`. Новый мутант `vacuum-manual-fit-after-proposal-uses-the-dock` (`scripts/mutation-gate.mjs`) возвращает `const space = dev.space` — лично прогнан, красный, как обязан (см. «Гейты») |
| **Medium** — §9.3 ТЗ «Добавить источник карты» (отдельная camera на карту) не реализовано; второй сценарий интеграции из тела issue отсутствовал | В `src/editors/vacuum-maps-section.ts` добавлен раскрывающийся блок «Добавить источник карты» (`<details class="vacroute-sources">`), список — кандидаты из `sources.candidates` (тот же `VacSourceResolution`, что и у общего picker + ленивой секции «Все камеры», без дублирующего вычисления), источник без читаемого map id показан неактивным с причиной | Код: `vacuum-maps-section.ts:101-120` (функция `addSource`, список `spare`) и `:212-227` (рендер). Переводы: `vac.route_add_source`, `vac.route_add_source_hint`, `vac.route_source_no_map` добавлены во все 4 языковых файла `src/i18n/support/*.json`, синхронность подтверждена `test/i18n.test.mjs` (счётчик ключей 74→77, тест прогнан в составе `npm test`). Ручной сценарий добавлен в `docs/TESTING.md:84-85` |
Обе находки закрыты по существу, не косметически: High доказан мутантом,
который ловит именно тот регресс, что описан в находке (коммит пространства
дока вместо маршрута); Medium — код читает тот же источник кандидатов, что и
уже проверенный picker, поэтому не вносит второй способ получить тот же
список камер.
## Унаследовано из r1 (без повторной проверки)
Со ссылкой на `docs/reviews/CODE-REVIEW-162-r1.md`, вынесенный на SHA `8e2892b9`
(де-факто; в тексте вердикта ошибочно назван `4032b810`, см. «Скоуп раунда»):
- Резолвер маршрутов и его чистые контракты (`src/vacuum-routes.ts`), включая
6 исходов резолвинга и `adoptLegacyRun` — не изменялись в дельте, доверяю
прежнему прочтению.
- Питоновское зеркало (`custom_components/houseplan/vacuum_routes.py`) и
серверный recorder/GC прогонов (`trails.py`) — не изменялись.
- Валидация `map_routes` на `config/set` и в обоих import-flow, а также
поведение `space-deletion.ts`/`space-reference-repair.ts` — не изменялись.
- Храповик ядровых файлов и ленивый граф (§3.4 ТЗ) — перепроверены заново в
этом раунде отдельным тестом (см. «Гейты»), так как дельта добавляла код
именно в эти файлы; расхождений с r1 не найдено (потолки соблюдены).
- 13 из 15 мутантов (кроме нового `vacuum-manual-fit-after-proposal-uses-the-dock`
и ранее заявленных восьми из спецификации) — не перепрогонялись, так как
защищаемый ими код не в дельте.
- backend-тесты (`pytest tests_backend`, 359 passed по r1) — не перепрогонялись,
дельта не трогает `custom_components/**/*.py` (подтверждено `git diff --stat`).
- Golden/performance — не гонялись ни в r1, ни здесь: вне AC, canonical запуск
остаётся пре-релизным гейтом.
## Находки
Нет. High: 0, Medium: 0.
Побочное наблюдение, не находка: в комментарии автора после `49f69f60`
заявлено «`npm test` 1856/0/1». Мой личный прогон (дважды, стабильно)
даёт 1854 pass / 0 fail / 1 skip (1855 всего). Расхождение на 2 теста не
объясняется дельтой (в ней нет новых `test(...)`, кроме одной изменённой
числовой проверки в `test/i18n.test.mjs`, которая ассертит количество ключей,
а не добавляет тест-кейс). Похоже на опечатку в отчёте, а не на нестабильный
прогон или пропущенные файлы: `fail: 0` в обоих случаях, набор тестов
одинаков. Не блокирует — актуальный результат зафиксирован мной лично и он
зелёный.
## Гейты — что прогнал и результат
- `npx tsc --noEmit` — чисто.
- `npm test` — **1854 pass / 0 fail / 1 skip** (см. наблюдение выше про
расхождение с отчётом автора).
- `npm run build` — собирается; `dist/houseplan-card.js` побайтово совпадает
с `custom_components/houseplan/frontend/houseplan-card.js`; JSON-сверка
`dist/houseplan-assets.json` ↔ `custom_components/.../houseplan-assets.json`
— идентичны. Третья копия (репозиторная `dist/`) — совпадает с только что
собранной, т.е. все копии синхронны.
- `node scripts/check-docs.mjs` — passed (7 файлов, 10 внешних ссылок);
прогнан, так как дельта трогает `src/**`.
- `node --test test/core-file-budget.test.mjs` — 7/7, потолки соблюдены
(`houseplan-card.ts` 13606/13659, `houseplan-editor-runtime.ts`
14320/14323).
- `npm run bundle:budget` — initial View 292978 Б gzip при потолке 294000±2000;
предупреждение про запас < 15000 Б воспроизводится, но это существующий,
учтённый долг (#367, потолок поднят коммитом `51b4cc7e` в этой же ветке до
начала r1), дельта добавила к нему ~7 байт — не новый долг этого раунда.
- `node scripts/smoke-select.mjs --base 8e2892b9 --head HEAD` — 217 смоков в
матрице; 8 «прямых совпадений» по символам дельты (`_vacCalConfirm`,
`_vacFit`, `_commitSpace`, `DevItem`): `smoke_vacuum_multifloor`,
`smoke_vacuum_firstuse`, `smoke_controls`, `smoke_danger_confirm_branches`,
`smoke_device_position_history`, `smoke_editor_gestures`, `smoke_fixed_floor`,
`smoke_vacuum`. Все восемь прогнаны лично — OK.
- `node scripts/mutation-gate.mjs --id=vacuum-manual-fit-after-proposal-uses-the-dock`
— «чистый прогон» OK, мутант краснеет, как обязан (1 из 1 пойман). Остальные
14 мутантов не перегонялись — их код не в дельте (унаследованы из r1).
- `node --test test/single-source-numbers.test.mjs` — 3/3 (уже входит в
`npm test`, прогнан отдельно для явности: дельта добавляет пользователю
видимое число — map id рядом с каждым источником в новом блоке — источник
этого числа один: `host._vacObservedMapId(dev, candidate.entityId)`,
вызывается и для отображения, и для записи маршрута, не кешируется отдельно
для каждого места).
### Что не проверял и почему
- `python -m pytest tests_backend` — не прогонял в этом раунде: дельта не
касается ни одного `custom_components/**/*.py` файла (подтверждено
`git diff --stat 8e2892b9..HEAD`). Результат r1 (359 passed) наследуется.
- `npm run invariants` — не прогонял: дельта не трогает рёбра комнат, записи
толщины, `layout`, `marker.space` или `open_spans`. Поле `route.space`
внутри `marker.vacuum.map_routes[]` — не то же самое, что `marker.space`, и
не является геометрическим ключом решётки.
- `npm run golden:verify` — не прогонял: дельта не меняет геометрию, стили или
слои плана — только текст/поведение диалога в редакторе устройства и текст
предупреждения. Визуальный рендер плана не затронут.
- Полная матрица `demo/smoke_*.mjs` (217 файлов) — не прогонял целиком: дельта
локальна (2 файла кода + переводы), а `scripts/smoke-select.mjs` явно указал
восемь прямых совпадений без «неопределённостей» и без слабых связей,
требующих отдельного решения.
- Остальные 13 из 15 заявленных мутантов — не перепрогонял: они охраняют код,
не входящий в дельту этого раунда (унаследовано из r1, где они уже были
лично прогнаны и покраснели).
- `docs/USER-GUIDE.ru.md` — сверил текст §«Калибровка» (строки 1497-1512):
формулировка уже достаточно общая («каждую карту нужно сопоставить
пространству и откалибровать именно в нём»), не называет конкретные кнопки
и не требует правки под новую «Добавить источник карты».
- Трейлеры `User-Visible: no` на обоих коммитах (`4b8cf6e0`, `49f69f60`)
проверены на соответствие проектной конвенции: во всей ветке этой задачи
только коммиты, впервые вводящие уже отраженную в `docs/CHANGELOG*.md`
возможность, помечены `yes` (`c38501ef`, `ca6ebc30`, `567b4568`); все
доводочные фиксы после них, включая более ранний `4032b810` (та же
категория бага — пространство калибровки), помечены `no`. Обе правки этого
раунда доводят уже анонсированное в «Не выпущено» поведение
(`docs/CHANGELOG.ru.md:16-19` уже обещает «каждую карту можно сопоставить
пространству и откалибровать её именно в этом пространстве» — без уточнения
механизма) до соответствия описанию, а не добавляют новую строку продукта.
Противоречия конвенции не нашёл.
## Итог
Обе находки r1 закрыты по существу и доказаны (одна — мутантом, ловящим
именно описанный регресс; другая — переиспользованием уже проверенного
источника кандидатов без дублирования). Новых находок в дельте нет. Гейты,
соразмерные объёму дельты, зелёные.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/162-vacuum-map-space-routing`, коммит `49f69f60dbd5` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `60cc4393ee53dac00659790870ee3dcb03ad3248`
```
git log --all --format='%H %T' | grep 60cc4393ee53
```
- ТЗ `docs/specs/162-vacuum-map-space-routing.md`, блоб `a8edcfbe6c37700095f6f8582258eac4fcb58ad9`
```
git log --all --find-object=a8edcfbe6c37700095f6f8582258eac4fcb58ad9 -- docs/specs/162-vacuum-map-space-routing.md
```