Files
houseplan-card/docs/reviews/CODE-REVIEW-627-r3.md
T
claude[bot] f8e089bdf2
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 26m52s
Проверка (CI) / Классификация изменённых файлов (push) Failing after 38s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Skipped
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (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) / Геометрия: TS/Python parity исполнена (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 19s
docs: review document for #627
Issue: #627
User-Visible: no
2026-09-24 16:52:11 +00:00

129 lines
22 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.
# CODE-REVIEW-627-r3
Материал: `b921ba71847df37416bed1c15e3d645953ffa1c9` (рабочая копия уже на нём; `git fetch`/`checkout` не выполнялись). Дерево `c76d64409884dfb646eb723f3e868003e923cce9`. Заход r3, блокирующих циклов израсходовано 1 из 4 (полный трек, лимит 4).
r2 (`docs/reviews/CODE-REVIEW-627-r2.md`, материал `47d07281b6ce…`, дерево `e19b817f43f3…`) был зелёным и цикла не образовал (#227). r3 существует не потому, что r2 что-то вернул автору, а потому, что автор запушил новый коммит: `b921ba71` — ребейз ветки на ушедший вперёд `origin/dev` (успевший вобрать #642, «диалог «Оптимизировать планы» — свой модуль») с пересборкой бандла и пересчётом потолков. `47d07281` в текущей истории не резолвится (ребейз его осиротил) — по [§2.10] это не находка, а обычное дело; материал ищется по дереву. Дерево, эквивалентное содержимому `47d07281`, живо в текущей истории как `70fe860bac6259fc1f32114a6b3fe191fe8d966a` (коммит «test(smoke): danger confirmation waits for the composite language gate (#627 r1 M1)», тот же M1-фикс, что и в r2), поэтому дельта раунда объявлена как `git diff 70fe860b..b921ba71`.
## Скоуп
ТЗ #627 (полный трек, из аудита T9 M2-A): не новая функциональность, а бюджетно-техническая работа над уже закрытыми job'ами J4/J6 (`docs/SCOPE.md`) — держит `bundle-budget.mjs` честным гейтом ленивых графов онбординга/редактора, без новой видимой поверхности. `User-Visible: no` на всех трёх коммитах — корректно, ниже подтверждено.
**Объявление дельты** (`git diff 70fe860b..b921ba71 --stat`): 74 файла — `scripts/bundle-budget.mjs` (17 строк: константа потолка + комментарий-причина), `scripts/monolith-baseline.json` (одно число), и 72 файла механической пересборки (`dist/**`, `custom_components/houseplan/frontend/**`, три копии бандла синхронны). `src/**/*.ts`, `test/**`, `demo/**` дельтой не тронуты — подтверждено и `git diff --stat`, и `node scripts/smoke-select.mjs --base 70fe860b --head b921ba71`:
```
Исполняемого frontend-диффа нет (src/**/*.ts не тронут).
Browser-smoke этим диффом не выбираются — это не «пропустить проверки»,
а «выбирать нечего»: смоки проверяют собранную карточку.
Тронуто файлов: 76.
```
Дельта локальна по содержимому (две строки логики — числовая константа и её обоснование), хотя коммит и называется «ребейз». Тем не менее я разобрал её не по минимуму, а вместе с двумя пробелами доказательств, унаследованными от r1/r2 (см. «Как проверялось»): впервые в истории задачи собранный бандл — совместный продукт #627 и #642 (до этого коммита #642 жил только в `origin/dev`, `82a48ee0` был выпущен поверх него, но ни разу не гонялся в браузере после реального слияния кода обеих задач в одно дерево), и два смока, тронутых ещё исходным коммитом `82a48ee0` и прямо относящихся к контракту задачи, ни разу не запускались ни в r1, ни в r2. Формально это не входит в дельту r3, но раз я всё равно поднимал браузер для проверки собранного дерева — закрыл оба пробела тем же прогоном; см. ниже.
## Как проверялось
Дешёвые гейты подтверждены зелёным Validate на этом самом SHA (run [36025312110](https://github.com/Matysh/houseplan-card/actions/runs/36025312110), `conclusion: success`, `headSha` = `b921ba71847df37416bed1c15e3d645953ffa1c9` — проверено напрямую через `gh run view --json`). Разбивка по джобам:
| Джоб | Результат |
|---|---|
| Классификация изменённых файлов | success |
| Предполёт: документация, провенанс, процесс | success |
| Переиспользование: это дерево уже проверено | success |
| Мутанты по диффу (6/6 шардов) | success |
| **Фронтенд: типы, юниты, мутанты, синхрон бандла** | **success** |
| Hassfest / HACS / pytest / geometry parity / browser smokes (3 шарда) / perf-smoke / golden | **skipped** (`workflow_dispatch`, не `heavy`) |
| Доказательство выполненных проверок | success |
Джоб «Фронтенд» содержит `npm test` → `npm run build` → `bundle-tree.mjs` + `npm run bundle:budget` → `npm run lint:unused` (в этом порядке, `.github/workflows/validate.yml:779-791`) — то есть typecheck, юниты, сборка, гейт потолка `LAZY_EDITOR_GZIP_CEILING` и гейт связности монолита (`bundleBytes` против `scripts/monolith-baseline.json`) уже подтверждены на этом точном SHA. Их я не перегонял.
Прогнано лично на `b921ba71` (дёшево, для точных чисел в этом документе и потому что новых чисел для CI-подтверждения годится не «похоже», а «совпало»):
| Гейт | Команда | Результат |
|---|---|---|
| Bundle sync (проверка свежести/синхрона копий) | `node scripts/bundle-sync.mjs` | `git status --short` пуст и до, и после — закоммиченный `dist/` уже байт-в-байт синхронен с `custom_components/**` и распакован в `demo/srv/assets` |
| Bundle budget | `npm run bundle:budget` | `lazy editor: 228621 B gzip (потолок 229600 B ±2000)`, `lazy onboarding: 27468 B gzip (потолок 28500 B ±2000)`, `initial View: 289418 B gzip (потолок 290400 ±2000)` — совпадает с числами коммита `b921ba71` дословно |
| Мёртвый код и связность монолита | `npm run lint:unused` | `monolith: delegates=154 portMembers=348 hostRefs=4869 portPrivates=94 harnessPrivates=101 bundleBytes=2508211` / `ok все числа равны базе` |
Арифметика коммита проверена построчно:
- `LAZY_EDITOR_GZIP_CEILING`: 229600, полоса 2000 → нижняя граница 227600; при замере 228621 запас сверху 229600−228621=**979**, запас снизу 228621−227600=**1021** — совпадает с текстом комментария в `scripts/bundle-budget.mjs:572` дословно.
- `monolith-baseline.json` `bundleBytes`: база `origin/dev` (`0f234f76`, тот же коммит, что и merge-base ветки с `origin/dev`) — `2499182` (проверено `git show 0f234f76:scripts/monolith-baseline.json`), текущее — `2508211`, разница `+9029` — совпадает с текстом коммита («десять новых чанков словарей и записи манифеста»); остальные пять чисел монолита (`delegates`/`portMembers`/`hostRefs`/`portPrivates`/`harnessPrivates`) не изменились ни на йоту относительно базы `dev` — тоже заявлено в коммите и подтверждено прогоном.
- Комментарий к `LAZY_EDITOR_GZIP_CEILING` над константой (строки 559–567 до правки) на промежуточном коммите `82a48ee0` содержит незаполненные плейсхолдеры `XXX` («замер XXX», «Центр оставляет XXX Б сверху и XXX Б до нижней границы») — артефакт промежуточного состояния ветки после первого ребейза на `origin/dev` с #642, где числа ещё не были пересчитаны. Это не находка: `82a48ee0` не является вершиной ни одного опубликованного ревью (ни r1, ни r2 его не рецензировали как отдельный SHA — их материал уже был позже осиротевшим `40959feb`/`47d07281`), а в материале **этого** раунда, `b921ba71`, плейсхолдеры заменены настоящими числами, что я и проверил выше побайтово. Упоминаю, чтобы явно снять вопрос, а не оставить его как тихо замеченную странность.
Смоки. `smoke-select` для дельты `70fe860b..b921ba71` не выбирает ничего (см. выше) — формально этого достаточно. Тем не менее я поднял браузер (нужно было для проверки живой пересборки бандла) и заодно закрыл два пробела в доказательствах, оставленные r1/r2:
| Смок | Почему запущен | Результат |
|---|---|---|
| `smoke_lazy_admin_locale.mjs` | Ядро AC4–AC7; первый прогон на дереве, где бандл собран **совместно** с #642 | OK, все 38 полей `true` |
| `smoke_danger_confirmation.mjs` | M1 (r1) / его фикс (r2) — держится ли регрессия закрытой на пересобранном бандле | OK, все 25 полей `true`, включая `{de,fr}GateSettledBeforeAsking` |
| `smoke_optimize_geometry_preflight.mjs` | Тронут `82a48ee0` (добавлено ожидание языкового гейта перед `card._editorRuntime.optimizePlans.open()` — новый порт из #642); **ни r1, ни r2 не упоминают этот файл ни разу** (`grep -n optimize` по обоим документам — пусто), хотя правка именно этого файла явно перечислена в теле коммита `b921ba71` («smoke_optimize_geometry_preflight: …, разрешено в коммите задачи») без единой ссылки на прогон | OK, все 16 полей `true` — пробел закрыт |
| `smoke_dialog_segments_i18n.mjs` | Единственный смок, явно названный в **ТЗ issue** («План автотестов»: «...`smoke_dialog_segments_i18n` (4 локали × 4 диалога; переключение языка должно ждать гейт)») для риска ровно той же природы, что и M1; тронут `82a48ee0`, но ни r1, ни r2 его не упоминают | OK, все 60 полей `true` — пробел закрыт |
| `smoke_color_picker.mjs`, `smoke_free_walls.mjs`, `smoke_space_scale_defaults.mjs`, `smoke_summary_panel.mjs`, `smoke_toggle_confirmation.mjs`, `smoke_ux_fixes.mjs` | Ещё 6 смоков получили тот же защитный «дождаться `aria-busy`» патч в `82a48ee0` (за пределами явного списка ТЗ — самостоятельный аудит автора), ни один не упомянут ни в r1, ни в r2 | Все 6 — OK, без единого `false`-поля |
Итого 9 браузерных смоков лично прогнаны на `b921ba71`, все зелёные без единого красного поля — включая четыре, которые пять раундов до этого (r1: три коммита, r2, теперь r3-до-этого-прогона) молча пропускали.
### Чего не проверял
- Индивидуальные прогоны 5 мутантов #627 по `--id` (`onboarding-loader-skips-namespace-ensure` и др.) — на этом SHA не перегонял; `src/**` в дельте `70fe860b..b921ba71` не тронут, а мутанты гейтят именно `src/**`. Опираюсь на личный прогон r1 (на уже осиротевшем SHA, но тем же дереве `src/**`) плюс на «Мутанты по диффу (6/6 шардов) — success» в CI на `b921ba71` — это диффовый прогон относительно доказанного предка, не индивидуальный запуск по `--id`, но за отсутствием изменений `src/**` он лишь переподтверждает то же дерево, а не проверяет что-то новое.
- `golden:verify`, перф-смок, `pytest tests_backend`, Hassfest/HACS, `geometry parity` — все skipped в CI (не `heavy`, `workflow_dispatch`) и не гейт этой дельты: ни эталонов рендера, ни Python, ни геометрии дифф не касается.
- Полный `smoke-select --base origin/dev --head HEAD` (а не `--base 70fe860b`) — не гонял: он воспроизвёл бы ровно то же самое (дельта `origin/dev..HEAD` содержит src-правки #627, уже разобранные в r1), а не что-то специфичное для этого раунда.
- Остальные ~250 несвязанных смоков вне плана ТЗ и списков связей — не прогонял, это предрелизная обязанность, не гейт ревью.
- Живой стенд Home Assistant — не поднимал.
## Находки
Нет. High 0, Medium 0.
Low-наблюдение, снятое без действия: патч «подождать `aria-busy` до 250×20мс» теперь инлайново продублирован в 12 файлах `demo/smoke_*.mjs` (проверено `grep -rl "hasAttribute('aria-busy')"`), при том что `demo/serve.mjs` уже экспортирует несколько общих утилит смоков (`watchPage`, `check`, `checkAll`, `reportPageErrors`, `finish`) — общий хелпер сюда напрашивается. Не поднимаю до Medium: сам приём (а не его вынесение в хелпер) был явно одобрен r1 как решение M1 («чинится тем же приёмом, что уже применён к девяти другим смокам»), дублирование не в дельте r3 (весь список — из `82a48ee0`, уже принятого в r1/r2), и это стиль тестового харнесса, а не продуктовый код.
## Что проверено и корректно
| AC | Проверено в r3 | Итог |
|---|---|---|
| AC1 (`bundle-budget.mjs` печатает и гейтит онбординг) | Не тронут дельтой; CI (`Фронтенд` job) переподтвердил `npm run lint:unused`/`bundle:budget` на этом SHA | Унаследовано + переподтверждено CI |
| AC2 (числовые потолки и запасы) | **Переисполнено лично**: `npm run bundle:budget` на `b921ba71` — редактор 228621 ≤ 229600 (запас к полосе 979/1021 Б, >500), онбординг 27468 ≤ 28500 (запас 34526−27468=27058 ≥ 6500, 244433−228621=15812 ≥ 15000), initial View 289418 (было 289457 до задачи — фактически **снижение**, не рост; в полосе потолка) — причина перецентровки редакторского потолка (#642) записана в комментарии константы дословно с числами | Доказан заново на текущем SHA |
| AC3 (9 ленивых чанков, владение графами) | Не тронут дельтой (`test/i18n-lazy-namespaces.test.mjs`, `test/bundle-assets.test.mjs` не изменились с r1); CI `Unit tests` зелёные на `b921ba71` | Унаследовано из r1 |
| AC4 (холодное открытие on ru/de/fr, первый кадр на языке) | Лично: `smoke_lazy_admin_locale.mjs` — OK на пересобранном бандле | Переисполнено |
| AC5 (live-переключение языка, составной гейт) | Лично: тот же смок + `smoke_danger_confirmation.mjs` (M1) + `smoke_optimize_geometry_preflight.mjs` + `smoke_dialog_segments_i18n.mjs` + 6 доп. смоков с тем же патчем — все OK | Переисполнено, шире, чем требовала дельта |
| AC6 (отказ обеих попыток → английский только в пространстве) | Не тронут дельтой; `smoke_lazy_admin_locale.mjs` (поля `failure*`) — OK в прогоне выше | Унаследовано + переподтверждено прогоном |
| AC7 (холодный View/en-редактор/Zigbee не тянут лишних чанков) | Не тронут дельтой; те же поля смока — OK | Унаследовано |
| Трейлеры | `git show <sha> -s --format='%B'` на `82a48ee0`/`70fe860b`/`b921ba71` — везде `Issue: #627`, `User-Visible: no` | Корректно |
| Changelog | `git diff 70fe860b b921ba71 --stat -- docs/CHANGELOG.md docs/CHANGELOG.ru.md` — пусто; верно при `User-Visible: no` и отсутствии видимых изменений | Корректно |
| Одно число — один источник | `grep -rn "229_600\|229600\|2508211" scripts test src` — по одному вхождению каждое, только в объявлении константы/базы | Корректно |
## Закрытие раунда r2
r2 был зелёным, «Находки: Нет» — закрывать нечего. Таблица приведена для формы, соответствия сокращённому шаблону:
| Находка r2 | Чем закрыта | Где видно |
|---|---|---|
| — | — | — |
## Унаследовано из r2
Без повторной проверки в r3 принято из `docs/reviews/CODE-REVIEW-627-r2.md` (материал `47d07281b6ce…` / дерево `e19b817f43f3…`, вердикт зелёный · заход r2 · High 0 · Medium 0), поскольку `src/**`, `test/**`, `demo/**` в дельте `70fe860b..b921ba71` не тронуты:
- AC3, AC6, AC7 — доказаны в r1, не пересчитывались повторно ни в r2, ни здесь.
- Полный реестр из 7 мутантов #627 (5 новых + 2 с обновлёнными якорями), лично прогнанных r1-ревьюером по `--id`, «поймано 1 из 1» каждый.
- `check-docs.mjs --screenshots=warn`, `check-inputs.mjs --coverage` (0), `CONTRIBUTING.md`/`tsconfig.test.json` — содержательно не менялись.
Не унаследовано, а переисполнено заново лично в r3 (см. «Как проверялось»): M1-фикс (`smoke_danger_confirmation.mjs`) и AC4/AC5 — потому что артефакт (собранный бандл) впервые стал совместным продуктом #627+#642, и это первый раз, когда кто-либо гонял браузерные смоки против именно такого дерева.
## Вердикт
Зелёный. Дельта раунда (`scripts/bundle-budget.mjs`, `scripts/monolith-baseline.json` + механическая пересборка) арифметически честна и подтверждена и CI, и лично. Ни одной находки. Дополнительно: два смока, явно относящихся к контракту задачи (один — по прямому тексту ТЗ, другой — по собственному упоминанию в теле коммита без прогона) и шесть смоков с тем же защитным патчем, ни разу не запускавшихся в r1/r2, лично прогнаны на этом SHA и зелёные — пробел в доказательствах предыдущих раундов закрыт, а не унаследован молча.
---
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/627-lazy-admin-locales`, коммит `b921ba71847d` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `c76d64409884dfb646eb723f3e868003e923cce9`
```
git log --all --format='%H %T' | grep c76d64409884
```
- Тело issue: `11ffae080ad26691a4b8dbd32f7736895c0c25a3f0cabe1b09da0e21666e6562`
- Вердикт конвейера: `green` · High 0