19 KiB
Код-ревью #593 — «Заменить все иконки библиотеки и расширить каталог до 60 плановых символов»
Заход: r1 · блокирующих циклов израсходовано 0/4 (после этого вердикта — 1/4)
Материал: 7df2740f1ccc3bf14caeca1ae824eed853dda50f (рабочая копия), один коммит,
245 изменённых файлов. Ветка была ребейзнута на ушедший вперёд dev (влился #594)
уже на этой вершине — конфликт был только в dist/**, custom_components/.../frontend/**,
обоих changelog и docs/images/screenshots.json; исходный код мебели трогали одни только
изменения этой задачи.
1. Скоуп ревью
ТЗ — редакция 4 в теле issue #593 (§1–§12), 11 критериев приёмки (AC1–AC11). Ревью полное: это первый заход code review, дельты от предыдущего раунда нет (спор был только в рамках самой спецификации, r1–r4 которой уже закрыт владельцем).
2. Как проверялось
Дешёвые гейты на 7df2740f уже зелёные (Validate run 35438169526: success), поэтому
npx tsc --noEmit, npm test, npm run build заново не гонялись. Пересобраны и
прогнаны:
| команда | результат |
|---|---|
npm run build && npm run bundle:sync |
бандл собран, три копии дерева совпали |
npm run furniture:check |
Furniture pack OK: 60 plan symbols, 33 menu icons (generated files current) |
npm run bundle:budget |
initial View 290836/291400 ±2000 · lazy editor 221927/222900 ±2000 · lazy furniture art 16943/17900 ±2000 — все три внутри полосы |
node scripts/check-docs.mjs --screenshots=strict |
Documentation checks passed (7 files, 12 external links) |
node scripts/smoke-select.mjs --base origin/dev --head HEAD |
прямое совпадение: smoke_decor.mjs, smoke_furniture_lazy_art.mjs, smoke_furniture.mjs |
node demo/smoke_furniture.mjs |
OK, все факты true, включая paletteShowsThirtyTwoCategories, newCategoriesAreVisible, requiredCategoriesArePresent, menuOnlyCategoriesStayHidden |
node demo/smoke_furniture_lazy_art.mjs |
OK, включая failedChunkDrawsNothingAtAll: true (Q2) |
node demo/smoke_furniture_polish.mjs |
OK (не в прямой выборке инструмента, но назван в AC6 автором — прогнан) |
node demo/smoke_decor.mjs |
OK (прямое совпадение по символу FURNITURE) |
npm run golden:verify (полная матрица, Chromium 151.0.7922.34 — совпадает с каноном) |
3 сцены different, 1 missing-baseline (новая сцена), остальные passed; разбор — находка §4.2 |
прямые вычисления по pack.json / test-build (см. §4.1) |
AC1–AC4 проверены исполнением скрипта, а не чтением |
Ручной проверкой кода (без исполнения): состав мутантов в scripts/mutation-registry.mjs
(5 новых + 1 исправленный), провенанс-документ пакета, i18n-диффы, docs/CHANGELOG*,
docs/STATUS.md, docs/USER-GUIDE.ru.md, docs/ARCHITECTURE.md.
3. Находки
Medium 1 — docs/ARCHITECTURE.md не обновлён, хотя ТЗ явно называет это условием
docs/ARCHITECTURE.md:249 до сих пор говорит: «Designer furniture artwork follows
the same shape (#474): the catalogue (ids, groups, default sizes) stays eager,
the 44 SVG drawings live in lazyFurnitureArtFiles…». После задачи их 60,
и союза с примитивами (12 retained) тоже больше нет — а абзац именно про этот
механизм (см. git diff — файл вообще не тронут этой задачей).
ТЗ §9 называет условие явно: «docs/ARCHITECTURE.md — если там названо число 44
или союз с примитивами». Условие выполнено (искал grep -n "44\|primitive" docs/ARCHITECTURE.md — единственное релевантное вхождение это строка 249), правка
не сделана. Расхождение будет видно любому, кто читает архитектурный документ
после docs/FURNITURE.md (там число уже 60) — источники разойдутся.
Воспроизведение: grep -n "44 SVG drawings" docs/ARCHITECTURE.md → строка 249,
не изменена в git diff origin/dev...HEAD -- docs/ARCHITECTURE.md (диф пуст).
Medium 2 — сцена tray-narrow-palette-en меняется, но не объявлена в списке golden-приёмки
ТЗ §8 объявляет ровно 10 сцен, которые разойдутся с эталоном («Объявляются заранее,
чтобы --expect-change был написан до прогона, а не подогнан под результат»).
node scripts/rebase-on-dev.mjs/хендофф этот список повторяют без изменений.
Прогнал npm run golden:verify на пересобранном бандле этой самой вершины,
Chromium 151.0.7922.34 (совпадает с каноном из docs/images/screenshots.json, то
есть это не шум чужой среды). Результат — три сцены different, а не две:
tray-narrow-palette-en different diffRatio 0.0022 ← НЕ в списке §8
furniture-categories-light different diffRatio 0.0034 (объявлена)
furniture-placement-preview-light different diffRatio 0.0031 (объявлена)
tray-narrow-palette-en (demo/golden/matrix.mjs:616-618) рендерит ту же
двухуровневую палитру категорий (furniturePalette: 'categories') на узком
вьюпорте — те же новые 33 иконки меню и 32 категории, что и у объявленной
furniture-categories-light, просто в другой раскладке. Соседние tray-* сцены
(tray-wide-selection-en, tray-medium-group-en, tray-medium-selection-ru) —
все passed, то есть разница локализована именно в контенте палитры мебели, а
не в шуме раскладки/шрифта.
Это не абстрактный риск: scripts/golden-acceptance.mjs → goldenAcceptanceRefusal
буквально отказывает приёмке при необъявленной different-сцене («съёмка
разошлась с принятыми эталонами в сценах, которые менять не собирались... приёмка
запрещена»). Владелец планирует принимать эталоны отдельным шагом со своей машины
(§8, риск №4) — с текущим списком --expect-change эта команда откажет именно на
tray-narrow-palette-en, и раунд придётся повторить только из-за неполного списка.
Воспроизведение: npm run build && npm run bundle:sync && npm run golden:verify
на этой вершине → artifacts/golden/golden-report.json, results[].id == 'tray-narrow-palette-en', status: 'different'.
Что делать: дописать tray-narrow-palette-en в перечень §8 (и, соответственно,
в --expect-change будущей команды приёмки). Кода это не касается — только текста
объявленного списка, который передаётся владельцу.
Оба High отсутствуют. Обе находки — Medium в скоупе задачи (одна прямо названа условием ТЗ §9, вторая — прямое следствие изменённого набора SVG меню, которое ТЗ само описывает в §8). По правилу PROCESS.md §12 (2026-08-19, #202) отдельный issue не заводится — обе чинятся в этой же ветке.
4. Проверено и подтверждено исполнением (AC1–AC11)
- AC1 (пакет 33/60, идентичность):
npm run furniture:check→OK: 60 plan symbols, 33 menu icons; прямым JSON-разборомpack.json—menu_icons.length === 33,symbols.length === 60,pack_version === '0.4.0',author/licenseкорректны. - AC2 (совместимость ID): вычислил множество старых публичных ID (44 дизайнерских
из
houseplan-0.3.0/pack.json+ 12 retained изLEGACY_FURNITURE/RETAINED_IDSнаorigin/dev) против новых 60 — расхождениеmissing: [],added: ['computer', 'hood', 'oven', 'cactus']. Ровно как требует ТЗ. - AC3 (mapping/категории):
menu_iconsминус используемыеsymbols[].menu_icon→ пустая ровноexercise; занятых категорий 32.cactus.menu_icon === 'plant'. Подтверждено также смоком (paletteShowsThirtyTwoCategories,menuOnlyCategoriesStayHidden,newCategoriesAreVisible— всеtrue). - AC4 (сохранённые размеры): вычислил объединённые старые размеры (44 из
0.3.0 pack.json + 12 из примитивов на
origin/devsrc/furniture.ts) и сравнил с новымиwidth_cm/depth_cmдля тех же 56 ID —mismatches: []. ТаблицаBEFOREвtest/furniture.test.mjsподтверждена независимым пересчётом, а не просто прочитана. - AC5 (inert-subset):
furniture:checkне ослаблен (генератор — тот же файл, правки только в константах идентичности пакета, см.git diff scripts/generate-furniture-assets.mjs— 3 строки). - AC6 (UX палитры):
demo/smoke_furniture.mjsиdemo/smoke_furniture_polish.mjs— оба зелёные на пересобранном бандле этой вершины. - AC7 (видимость 60 вариантов): golden-сцена
furniture-new-symbols-lightдобавлена в матрицу (версия 63→64), содержит все 4 новых ID; статус в отчёте —missing-baseline(ожидаемо, эталона ещё нет). Остальной мебельный срез разобран в §3 (Medium 2).test/pdf-scene.test.mjs/test/pdf-svg-path.test.mjsне менялись и не нуждались — уже параметризованы по каталогу, не по примитивам. - AC8 (lazy split, отказ чанка):
demo/smoke_furniture_lazy_art.mjsзелёный,failedChunkDrawsNothingAtAll: true,foreignBuildFallsBackLikeNetworkFailureтоже смотрит наfurniture === 0.furnitureArtIsLazyвsrc/furniture.tsтеперьBY_ID.has(id)— унифицировано для всех 60. - AC9 (бюджеты-гейты):
lazyGraphCeilingViolation— новый экспорт, используется вassertBundleBudget;npm run bundle:budgetна собранном бандле подтверждает обе новые проверки внутри полосы (лог в §2). Тестtest/bundle-assets.test.mjsсверяет и реальныйdist/houseplan-assets.json, и синтетику по обе стороны полосы, и «зазор > 500 Б» с обеих сторон — как требует §7 ТЗ. - AC10 (провенанс/грант/лицензия):
assets/furniture/houseplan-0.4.0/README.mdнесёт ссылку на грантissuecomment-5739841899, имя архиваhouseplan-furniture-0.4.0.zip, SHA-25669BA5E0C…. Тестrelease provenance is normalized...вtest/furniture-assets.test.mjs— 6 assert (2 equal + 4 match) против 5 у версии наorigin/dev(2 equal + 3 match): число проверок не уменьшилось, требование ТЗ §4 п.10 выполнено буквально. - AC11 (доказательства/команды): хендофф от 2026-09-19 называет команды и
результат; после ребейза — второй хендофф с полным списком (
furniture:check,npm test2787/2786/1 skipped,bundle:sync,bundle:budget,docs:accept -- --identical11/11,mutation-gate.mjs --check,process-gate.mjs). Я independently повторил релевантную часть (см. §2) на этой же вершине.
Ловушка союза LEGACY_FURNITURE ∪ GENERATED_FURNITURE_CATALOG (§5 ТЗ) закрыта
буквально: LEGACY_FURNITURE, RETAINED_IDS, тип Prim, box(),
primitivePathD удалены из src/furniture.ts, FURNITURE — прямое отображение
GENERATED_FURNITURE_CATALOG. Мутант furniture-legacy-shadows-designer-art
воспроизводит именно эту ловушку патчем на src/furniture.ts и ловится
test/furniture-assets.test.mjs/test/furniture.test.mjs (подтверждено автором
как «1 из 1» на этой же вершине после ребейза, и я не нашёл оснований сомневаться
— код мутанта соответствует описанию, node --check scripts/mutation-registry.mjs синтаксически чист).
i18n: 7 ключей × 4 локали = 28 добавленных строк, посчитано git diff построчно —
ровно совпадает с §6 ТЗ. furn.cat_exercise не добавлен — верно, категория скрыта.
5. Чего не проверял и почему
npm run typecheck/npm test/npm run build"с нуля" — не гонял: Validate на этой же вершине (7df2740f) зелёный (run 35438169526), а рекомендация задания прямо разрешает не повторять эти три гейта при зелёном Validate на материале.npm run buildя всё же выполнил (дважды) — но не ради этого гейта, а чтобы получить свежий бандл для golden/smoke, поэтому неявно он подтверждён тоже (чисто, без ошибок typecheck).npm run gate:small -- --smokes, полныйdemo/smoke_*(250 файлов) — не требовалось: diff не «широкий» символ (11 попаданий на изменённых строках, порог — 50), инструмент назвал 3 сцены прямого совпадения, все три прогнаны. Дополнительно прогналsmoke_furniture_polish.mjs, названный в AC6 автором.python -m pytest tests_backend— diff не касаетсяcustom_components/houseplan/**/*.py(git diff --statподтверждает: ни одного Python-файла в дифе).npm run invariants -- --config …— diff не касается геометрии/ссылок (рёбра комнат, толщина,layout,marker.space,open_spans); мебель — decor без геометрической модели (ТЗ иdocs/FURNITURE.mdэто прямо фиксируют).node scripts/mutation-gate.mjs— не перегонял сам (это дорогой полный прогон); полагаюсь на то, что диффовый mutation-gate — часть Validate на review-кандидате (.github/workflows/validate.yml, шагmutation-gate.mjs --changed=$base..$HEAD_SHA), а он зелёный на этой же вершине, плюс автор отдельно перепроверил ключевые мутанты после ребейза.- Приёмка golden-эталонов (
golden:accept) — вне досягаемости ревьюера (нужна машина владельца с полным Linux CI артефактом), что верно и названо в самом ТЗ (§8, риск №4).golden:verify(advisory, легален где угодно) я прогнал — и это как раз дало находку Medium 2. node scripts/process-gate.mjs --issues— требуетghс правами записи в контексте задачи; не относится к предмету код-ревью.
6. Вердикт
Одиннадцать AC доказаны — частично чтением, частично (там, где это имело смысл: подсчёт множеств ID/размеров, смоки, бюджеты, golden) независимым исполнением на этой же вершине, не только цитированием хендоффа. Явных дефектов поведения не нашёл: ловушка союза каталогов закрыта, Q1/Q2/Q3 реализованы буквально, бюджеты стали настоящим гейтом, провенанс не ослаблен.
Две находки Medium — обе в скоупе, обе не требуют выхода за пределы задачи:
пропущенная правка docs/ARCHITECTURE.md (условие ТЗ §9 сработало, но не
выполнено) и неполный список golden-сцен для будущей приёмки эталонов (реальный,
воспроизведённый на каноническом Chromium диф, который иначе откажет команде
golden:accept). High нет.
Вердикт: жёлтый · заход r1 · блокирующих циклов 1/4 · High: 0 · Medium: 2 → в задаче
Материал раунда
- Ветка:
issue/593-furniture-pack-0.4.0, коммит7df2740f1ccc— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
4141dff7cb0ad45e725cc54b9fac4ce7a156a9ccgit log --all --format='%H %T' | grep 4141dff7cb0a - Тело issue:
3a8d99615191f61cc781ac873210df277ce0951d828113e3efa89700a5b7b6a3 - Вердикт конвейера:
yellow· High 0