Files
houseplan-card/docs/reviews/CODE-REVIEW-474-r1.md
claude[bot] c82ba84cc7
Проверка (CI) / Классификация изменённых файлов (push) Successful in 1m46s
Проверка (CI) / Предполётные проверки: документация, провенанс, процесс (push) Failing after 2m20s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 1m3s
Проверка (CI) / HACS: валидация репозитория (push) Failing after 23s
Проверка (CI) / Hassfest: манифест интеграции (push) Failing after 34s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 16m17s
Проверка (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) Failing after 9m53s
docs: review document for #474
Issue: #474
User-Visible: no
2026-09-06 13:38:29 +00:00

17 KiB
Raw Permalink Blame History

CODE-REVIEW-474-r1

Issue: #474 — «Стартовый граф: арт мебели уходит в ленивый чанк» Этап: code · заход r1 · блокирующих циклов израсходовано 0 из 4 Ветка: issue/474-lazy-furniture-art, HEAD 605a9a1c (после ребейза на origin/dev = ab01586b) Спецификация принята зелёным на r3 (docs/reviews/SPEC-REVIEW-474-r3.md); это первый заход кодового ревью — авторская попытка ранее вернулась не по коду, а по конфликту ребейза (docs/specs/README.md), решённому без содержательных изменений реализации, поэтому разбор полный, не дельта.

Скоуп

Диапазон git diff origin/dev...HEAD (62 файла): разрез генератора мебели на каталог (eager) и арт (ленивый чанк), новый FurnitureArtRuntime (src/furniture-art-runtime.ts), три точки готовности арта (приём конфига, редакторный adopt, бут-гейт), перевод магнита furniture-placement.ts на каталог, бюджет бандла (потолок 300 500 → 290 500), семь мутантов-свидетелей, новый смок smoke_furniture_lazy_art, unit-тесты, golden/harness.mjs-гейт на .dfurn, i18n-тост в четырёх языках, docs/ARCHITECTURE.md/docs/FURNITURE.md.

Не в скоупе диффа и не проверялось повторно: сам арт/каталог мебели (не менялся), редактор мебели вне точки adopt, бэкенд (.py не тронут).

Как проверялось

Гейты, прогнанные лично на этом SHA (список честности):

Гейт Команда Результат
Типы npx tsc --noEmit зелёный
Unit npm test 2139 тестов, 2138 pass, 1 skipped (пред-существующий), 0 fail
Сборка npm run build зелёная, tsc+rollup
Бюджет бандла node scripts/bundle-budget.mjs initial View 289 760 Б (потолок 290 500±2000, стена 301 066, запас 11 306 Б); предупреждение о низком запасе (#367) — известный, не этой задачи долг
Дерево бандла npm run bundle:sync + git status пусто — committed dist/, custom_components/.../frontend, demo/srv/assets побайтово совпадают со свежей сборкой
Документация node scripts/check-docs.mjs зелёный, 7 файлов, отпечаток скриншотов свежий
Смок задачи node demo/smoke_furniture_lazy_art.mjs OK, все 15 проверок (AC2–AC5, AC11)
Смоки, названные в AC7 smoke_furniture, smoke_furniture_polish, smoke_decor, smoke_decor_default_persist, smoke_lazy_editor_chunk, smoke_preloader_lifecycle все OK
smoke-select.mjs --base origin/dev --head HEAD инструмент отбора 9 прямых совпадений — все на _showToast (общее имя, слабая связь: свежий вызов composeUnsub — аддитивный, реализацию _showToast не трогает — не гонял все девять); 4 «зарегистрированные связи» — EditorRuntimeLoader/LanguageRuntime не изменялись в этом диффе, smoke_lazy_editor_chunk уже прогнан по AC7, остальные три (smoke_entry_stale, smoke_french_locale, smoke_german_locale) не гонял — связь по аналогии контракта, не по изменённому модулю
Мутанты §4.6 (7 шт.) node scripts/mutation-gate.mjs --id=<each> по одному (флаг --id= берёт первое вхождение) все семь: «поймано 1 из 1» — furniture-art-eager-import, furniture-art-fallback-never-settles, furniture-art-no-retry-nonce, furniture-art-boot-gate-ignored, furniture-placement-needs-art, furniture-art-fingerprint-unchecked, furniture-art-editor-adopt-skipped
Golden npm run golden:verify (полный набор — diff меняет рендер мебели и бут-гейт) 15 сцен «different», identical к прогону на чистом origin/dev в отдельном worktree (см. ниже) — 0 расхождений от диффа; все furniture-*/decor-*-мебельные сцены — passed
Инварианты модели не гонял diff не трогает рёбра комнат, записи толщины, layout, marker.space, open_spans — магнит мебели проверяет только каталог размеров, геометрию стен не меняет
pytest tests_backend не гонял custom_components/**/*.py не тронут
npm run inventory прогнан 2138 unit / 310 pure-backend / 211 HA-harness / 227 browser smokes — числа не копировал вручную

Golden: изоляция пред-существующего дрейфа

golden:verify не даёт прогон по одному сценарию (policy.mjs требует полный набор для verify), поэтому гонял целиком. Список из 15 «different» — исключительно isometric-* (10) и *-color-popover-* (5) — не пересекается с темой задачи. Чтобы не приписать их этому диффу, поднял origin/dev (ab01586b) в отдельном git worktree, собрал и прогнал тот же golden:verify: результат — те же самые 15 сцен «different», побуквенно то же множество id. Дрейф пред-существующий (среда/Chromium), не regressии. Worktree убран (git worktree remove --force).

Находки

Low — scripts/bundle-budget.mjs: печать не называет новый ленивый граф

Файл: scripts/bundle-budget.mjs, функция сборки lines для CLI-вывода и блок GITHUB_STEP_SUMMARY (около строк 322–344).

Манифест и assertBundleBudget уже возвращают lazyFurnitureArtGzipBytes (добавлено этим диффом, покрыто тестом в test/bundle-assets.test.mjs), но печатаемые строки CLI перечисляют только lazy editor/lazy locale/ lazy isometric — новый граф мебели не появляется ни в консоли, ни в таблице GITHUB_STEP_SUMMARY, хотя структурно стоит рядом с остальными тремя.

Сценарий отказа: это не влияет на корректность гейта — assertBundleBudget по-прежнему бросает исключение, если lazyFurnitureArtFiles пуст или пересекается с initial (проверено чтением и тестом). Cтрадает только наблюдаемость: инженер, читающий вывод bundle:budget в CI-сводке при следующей правке бюджета, не увидит вес чанка мебели рядом с остальными тремя графами и должен будет открыть JSON-манифест вручную.

Не блокирует; можно поправить в этой же задаче (одна строка в lines и в таблице GITHUB_STEP_SUMMARY) или снять с явной пометкой «не печатаем — граф редко меняется» — оставляю решение автору.

Что проверено и корректно

  • Разрез генератора (§4.1). scripts/generate-furniture-assets.mjs пишет furniture-plan-catalog.generated.ts (eager, id/группа/категория/размеры) и furniture-plan-art.generated.ts (GENERATED_FURNITURE_ART + отпечаток), импортируемый только динамически из furniture-art-runtime.ts и статически из houseplan-editor-runtime.ts. Инвариант «id каталога == ключи арта» — тест в test/furniture-assets.test.mjs, проверен запуском.
  • FurnitureArtRuntime (§4.2). Контракт ready|pending|fallback зеркалит LanguageRuntime: art() синхронный и не запускает загрузку, ensure() идемпотентен (один in-flight промис — тест «only a single in-flight load»), adopt() синхронный и терминально отвергает чужой отпечаток, fallback осевший (второй провал не откатывает раньше _failed, не ретраится дальше — проверено и мутантом, и unit-тестом). Нонс на attempt 1 — через content-hashed токен bundle-manifest.mjs подставляет так же, как для de/fr/iso (проверено чтением диффа bundle-manifest.mjs — счётчик замен теперь 1/1/1/1/1/1, шестой — новый).
  • Три точки готовности (§4.3). Приём конфига — ensureFurnitureArtFor в _seedDecorStyle, вызывается на всех путях, где в _serverCfg может появиться новая мебель: первичный кэш (LS_CFG), _adoptStructuralResponses (оба live-sync входа — _syncLiveConfig-подобные вызовы на строках 4314 и 4491 — заведены через один и тот же метод). Пути, где _serverCfg переписывается напрямую (_setAreaLifecycleConfig, _syncNewDevices, _ackNewDevice, _writeConfig's canonicalize) намеренно не вызывают _seedDecorStyle — они правят только settings/marker_area_snapshot, не spaces[].decor, проверено чтением каждого из четырёх сайтов. Редактор — статический импорт + FURNITURE_ART_RUNTIME.adopt(...) на уровне модуля houseplan-editor-runtime.ts, которое само является лениво импортируемым корнем (houseplan-card.ts:697), то есть adopt гарантированно выполняется до любого рендера палитры/призрака. Бут-гейт — furnitureArtBootPending в условии _bootWatch, BOOT_MAX_MS остаётся безусловным потолком (проверено чтением: elapsed >= BOOT_MAX_MS || стоит первым слагаемым ||).
  • Магнит (AC6). furniture-placement.ts проверяет furnitureSymbol (каталог), не furnitureGraphic (арт) — размещение designer-предмета работает в pending, подтверждено unit-тестом и мутантом furniture-placement-needs-art.
  • Отказ (§4.4, AC4/AC5). Смоком подтверждено: два провала → fallback, вуаль снята, устройства живы, только legacy-предмет рисуется, ровно один тост; чужой отпечаток → терминальный fallback за один запрос без ретрая.
  • Бюджет (AC1). Измерено лично: initial View 289 760 Б, потолок 290 500, стена не тронута, lazyFurnitureArtFiles непуст и не пересекается с initial — assertBundleBudget бросает при нарушении (проверено мутантом furniture-art-eager-import).
  • Golden (AC8). Гейт в demo/golden/harness.mjs требует все [data-kind="furniture"] активного пространства после снятия вуали — если бы фолбэк не различался от пустого кадра, гейт сам бросил бы; полный golden:verify подтвердил 0 диффа от задачи (см. раздел про изоляцию выше).
  • Мутанты (AC9). Семь строк в §4.6 = семь id в mutation-gate.mjs = семь «поймано 1 из 1» при личном прогоне каждого по отдельности.
  • Ядро (AC10). houseplan-card.ts — 13 656 строк, тест core-file-budget.test.mjs зелёный (потолок не превышен).
  • i18n. toast.furniture_art_load_failed — во всех четырёх словарях (en/ru/de/fr), формулировка по образцу toast.locale_load_failed.
  • Трейлеры. Все три продуктовых коммита несут Issue: #474 и User-Visible: no; changelog не тронут — соответствует §7 спецификации (визуальных изменений нет).
  • Документация. docs/ARCHITECTURE.md и docs/FURNITURE.md описывают новую ленивую границу; отпечаток скриншотов обновлён в том же коммите (605a9a1c), check-docs.mjs зелёный.
  • Единственный источник числа. Диф не добавляет и не меняет ни одной видимой пользователю величины (UX не меняется по AC2/§6.1) — гейт single-source-numbers.test.mjs прогнан в составе npm test, не задет.

Чего не проверял

  • python -m pytest tests_backend — бэкенд не тронут этим диффом.
  • npm run invariants — диф не касается геометрии стен/комнат/layout; единственная геометрическая точка соприкосновения (furniture-placement.ts) теперь читает только каталог размеров, ключи решётки не затронуты.
  • Три «зарегистрированные связи» инструмента отбора смоков — smoke_entry_stale, smoke_french_locale, smoke_german_locale — оставлены непрогнанными: связь идёт по аналогии архитектурного контракта (EditorRuntimeLoader/LanguageRuntime), а не по изменённому файлу; ни editor-runtime-loader.ts, ни language-runtime.ts в диффе нет.
  • Девять «прямых совпадений» на _showToast — общее имя, использованное по всей карточке; прогнан только тематически релевантный (входит в AC7-список), остальные восемь не гонял — правка добавляет вызов composeUnsub, не трогая саму _showToast.
  • Performance-профили — не названы в AC и не задет чувствительный к перфу путь (бут-гейт ограничен тем же BOOT_MAX_MS, что и раньше).
  • Ручное открытие карточки в браузере вне смоков/golden — не делал; браузерные смоки и golden воспроизводят все сценарии AC через настоящий Chromium.

Вердикт

Все 11 AC доказаны исполняемыми гейтами, которые я прогнал лично и которые умеют падать (проверено мутантами на защитных контрактах и сверкой golden против чистого dev для исключения пред-существующего дрейфа). Единственная находка — Low, чисто наблюдательная, не по AC и не по корректности.

Зелёный.


Материал раунда

  • Ветка: issue/474-lazy-furniture-art, коммит 605a9a1c786c — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 780f546ed4d2a9be72796f222d3d8bb0331c0268
    git log --all --format='%H %T' | grep 780f546ed4d2
    
  • ТЗ docs/specs/474-lazy-furniture-art.md, блоб b4e8a03e921dd45220d33cd55afe95fd60440bbe
    git log --all --find-object=b4e8a03e921dd45220d33cd55afe95fd60440bbe -- docs/specs/474-lazy-furniture-art.md