17 KiB
CODE-REVIEW-606-r1 — «Показать тренажёр и исправить перепутанные bookshelf/shelf_floor»
Issue: #606
Этап: code · трек: полный · заход r1 · блокирующих циклов израсходовано 0 из 4
Материал: диапазон origin/dev..HEAD, вершина 6d6d6a131778b8fca65c3489f206380949159cb2 (рабочая копия на этом SHA, git status чистый на всём протяжении ревью)
Коммиты: 20457660 fix(furniture) User-Visible: yes · 748ebe86 test(furniture) · 6d6d6a13 test(golden) — все с трейлерами Issue: #606
Скоуп
Задача — коррекция уже выпущенного пакета мебели houseplan-0.4.0 (#593):
переименование ошибочно подписанного cactus в exercise без изменения
рисунка, обмен содержимым файлов bookshelf.svg/shelf_floor.svg, публикация
пакета 0.4.1, чтение старого symbol: cactus как read-only совместимого
псевдонима на всех поверхностях рендера, обновление локализации и обоих
changelog. Предыдущий этап — ТЗ, зелёный вердикт r1 (docs/reviews/SPEC-REVIEW-606-r1.md,
закоммичен этой же веткой). Это первый заход код-ревью, раздела «Унаследовано
из r0» не требуется.
Как проверялось
Дешёвые гейты уже подтверждены на этом SHA: Validate run 35601256965
(headSha сверен через gh run view --json headSha = 6d6d6a13...) —
success, включая фронтенд (typecheck/unit/mutants по диффу/бандл-синхрон) и
provenance, process-gate. Поэтому npx tsc --noEmit, npm test и
npm run build со сверкой копий бандла не перегонялись повторно как отдельный
общий прогон; вместо этого бюджет пошёл на targeted-перепроверку и на то, что
Validate в этом прогоне не покрывает (browser smoke, golden — оба skipped в
этом run, поскольку это не heavy-гейт).
Что прогнал сам, помимо чтения кода:
npm run furniture:check→Furniture pack OK: 60 plan symbols, 33 menu icons— подтверждает AC1 независимо от отчёта автора.diffбайт-в-байт междуassets/furniture/houseplan-0.4.0иhouseplan-0.4.1:plan/exercise.svgидентичен старомуplan/cactus.svg;plan/shelf_floor.svg(0.4.1) идентичен старомуplan/bookshelf.svg;plan/bookshelf.svg(0.4.1) идентичен старомуplan/shelf_floor.svg;menu/exercise.svgне тронут — подтверждает AC2 напрямую, а не через тест автора.npm run build+npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs+node --testнаtest/furniture-assets.test.mjs,test/furniture.test.mjs,test/furniture-path-join.test.mjs,test/pdf-scene.test.mjs,test/golden-matrix.test.mjs,test/i18n.test.mjs→ 161/161 passed.- Дисциплина «тест умеет падать»: временно откатил тело
canonicalFurnitureIdв скомпилированномtest-build/furniture-id.jsдоreturn id;(только build-артефакт вне git, не источник) и перезапустилtest/furniture.test.mjs— новый тест#606: saved cactus resolves…покраснел (43 passed / 1 failed), файл сразу восстановлен из бэкапа;git statusпосле — чистый. node scripts/smoke-select.mjs --base origin/dev --head HEAD→ «Зарегистрированная связь»:demo/smoke_furniture.mjs ← canonicalFurnitureId, обоснование вscripts/smoke-links.mjs— символ читается по DOM, а не импортом. Больше связей инструмент не назвал; широких совпадений (>52 смоков) нет. Решение: прогнать именно этот смок целиком (единственный прямо связанный с диффом;demo/smoke_furniture_polish.mjsиdemo/smoke_furniture_lazy_art.mjs, названные автором, не прогонял отдельно — не в выдаче инструмента и не требуются ни одним AC как отдельные свидетели, толькоsmoke_furnitureназван в ТЗ явно).npm run bundle:sync(build +bundle-sync.mjs) → пересобранныйdist/**/custom_components/houseplan/frontend/**байт-в-байт совпал с закоммиченным (git statusчистый) — независимое подтверждение синхронизации бандла помимо отчёта Validate.node demo/smoke_furniture.mjsцеликом (пересобранный бандл, Linux/headless Chromium) → OK, все 90+ полейcheckAll, включая новые для #606:exerciseCategoryIsVisible,paletteShowsThirtyThreeCategories,exerciseCategoryHasOneVariant,plantCategoryHasOnlyPlant,savedCactusDrawsInView,savedCactusUsesExerciseArtwork,legacyPropertiesShowExercise,unrelatedSaveKeepsLegacyIdAndTransform,explicitVariantChangeWritesExercise— всеtrue. Проверил, чтоcheckAll(res)вdemo/serve.mjsтребуетtrueпо умолчанию для каждого ключа возвращённого объекта — падение любого нового поля красит смок.node demo/golden/run.mjs --mode=capture --scenario=furniture-corrected-art-light(у verify нет флага диагностики по одной сцене —policy.mjsотказывает;--mode=capture --scenario=— штатный путь для одиночного диагностического кадра) → эталона нет («missing-baseline», ожидаемо: сцена новая, автор явно не принимал её). Полученныйartifacts/golden/actual/furniture-corrected-art-light.pngпросмотрен лично: два одинаковых рисунка тренажёра (старыйcactus, повёрнутый/отражённый, и новыйexercise) — рисунки идентичны, как и требует AC4; книжный шкаф (повёрнут, отражён) и напольный стеллаж внизу — два явно разных рисунка, как требует AC2/AC3; наложения на стены нет. Это независимое визуальное подтверждение, не принятие эталона — файл лежит вartifacts/, которая в.gitignore, ничего не закоммичено.
Чего не проверял и почему: полный npm run golden:verify по всей матрице (260 сцен) не гонял — задача не трогает ничего вне сценариев мебели, а прогонявшаяся автором матрица уже показала точечные отличия только в несвязанных диалоговых сценах (device-dialog-*, settings-help-* и т.п.), что подтвердилось и в моём фрагментарном логе того же прогона матрицы (я успел получить только хвост, но не увидел там furniture-corrected-art-light вообще, потому что эта сцена не имеет эталона и в списке verify не появляется как «different»/«passed» — с --mode=capture это подтвердилось явно как «missing-baseline»); полный прогон не добавил бы уверенности сверх точечной проверки. python -m pytest tests_backend не гонял — диф не трогает custom_components/**/*.py. Perf-профили — не названы в AC и не в чувствительном пути. demo/smoke_furniture_polish.mjs/demo/smoke_furniture_lazy_art.mjs — не перепрогонял, см. решение выше; автор отчитался о зелёном прогоне (WSL для lazy_art, с уже известной путевой проблемой на Windows-исполнении, не связанной с #606).
Находки
Ничего не нашёл, что тянуло бы на High или Medium.
Low: изменение docs/USER-GUIDE.md (EN) ограничивается одним абзацем без
переработки более старого текста об «32 категориях» в других местах файла —
не нашёл такого упоминания при чтении изменённого раздела и соседних
абзацев, так что похоже это не отдельная находка, а просто нечего чинить;
явно не помечаю как Low, поскольку не нашёл подтверждающей строки.
Что проверено и корректно
- AC1 —
furniture:check(прогнан лично) и независимое чтениеpack.json:exercise → exercise,plant → plant,cactusотсутствует среди 60 видимых id, все 33 категории имеют варианты (categories.size === 33вtest/furniture-assets.test.mjs, тест сравнивает с независимымMANIFEST). - AC2 — прямой байтовый
diff(не тест автора) подтвердил переименованиеcactus→exerciseбез изменения геометрии и обмен содержимымbookshelf↔shelf_floor; размеры вpack.json(70×120,100×35) иback: topне изменились;svg/menu/*не тронуты. - AC3 —
demo/smoke_furniture.mjs, прогнан лично: категория «Тренажёр» видима, содержит один вариантexercise(неcactus), «Растение» содержит толькоplant; превью категорий используют то же арт-исходное представление. - AC4 — единственный путь превращения id в рисунок —
canonicalFurnitureId(src/furniture-id.ts), подключён во всех точках чтения:furnitureSymbol,furnitureArtIsLazy,furnitureGraphic,furniturePathD(src/furniture.ts), выбор в диалоге свойств (src/decor-image-editor.ts), PDF/static (src/pdf/pdf-scene.ts). Проверил, что других мест чтения арта по id нет (grepпоGENERATED_FURNITURE_ART[,FURNITURE_ART_RUNTIME.art(,furnitureGraphic(— все проходят либо напрямую через резолвер, либо через функции, которые уже его вызывают внутри).isDesigner/furnitureArtBootPendingполучает уже канонизирующуюfurnitureArtIsLazy, так что boot-veil у старогоcactusждёт арт как у любого известного символа — риск 1 из ТЗ закрыт кодом, а не только тестом. Свойства старого предмета показывают «Тренажёр» (canonicalFurnitureId только для вычисленияselected, само значениеdialog.symbolне переписывается до явного@change) — риск 2 из ТЗ закрыт; обычное сохранение не переписывает id — smokeunrelatedSaveKeepsLegacyIdAndTransformподтверждает на всех x/y/w/h/angle/flip/color/opacity/width_cm полях. Явная смена варианта пишетexercise— подтверждено смоком и отдельно unittest/furniture.test.mjs. Визуально (мой захват golden-кадра) рисунки старого и нового id идентичны, а bookshelf/shelf_floor различны, включая повёрнутый/отражённый экземпляр — риск 3 из ТЗ закрыт не только тестом матрицы, но и просмотром. - AC5 — RU/EN/FR/DE получили
furn.cat_exercise/furn.sym_exerciseбез fallback (test/i18n.test.mjsбольше не исключаетfurn.sym_cactusиз проверки «не совпадает с английским», потому что ключ убран целиком — ТЗ разрешает это явно: «могут оставаться» не значит «обязаны»).docs/FURNITURE.md,docs/USER-GUIDE.ru.md,docs/USER-GUIDE.md,docs/STATUS.md, обаCHANGELOGиREADME.mdнового пакета описывают фактический результат (33/33 категории, точные три правки, происхождение 0.4.0 не переписано). Оба changelog в одномUser-Visible: yesкоммите20457660(провереноgit show --stat). - AC6 —
furniture:check,typecheck(частьnpm run build),npm test-эквивалент (targeted + прогнан Validate),build,bundle:sync/bundle:budget(Validate), мебельный smoke (лично) и golden (лично, точечно) — все зелёные на этом SHA. Изменившийся золотой набор — ровно одна новая сцена без эталона, автор явно не принял её автоматически (принятие эталонов не входит в эту задачу). - Мутационный тест
furniture-exercise-misfiled-in-plant(переименован изfurniture-cactus-lands-in-exercise) бьёт по новому файлу пакета и новому инварианту; Validate прогнал мутанты по диффу зелёным на этом SHA (все 6 шардов «Мутанты по диффу» success). - Инфраструктурный блокер (рассинхрон
mutation-gate.ymlмеждуmain/devпосле #604) устранён отдельным инфраструктурным коммитом74fcff55вне этой ветки — к #606 не относится, в диапазоне ревью не участвует. - Инварианты геометрии/толщины стен (
npm run invariants) не запускал: дифф не трогает рёбра комнат,layout,marker.space,open_spans— только декор-слой (мебель), для которого этот гейт не применим. - «Одно число — один источник»: диф не вводит новых видимых числовых величин
(размеры
70×120/100×35не новые, копируются из спецификации #593/#606 как константыpack.json, не дублируются вручную нигде ещё) — проверка неприменима.
Итог
Изменение точное, byte-for-byte проверяемое независимо от тестов автора,
контракт совместимости (canonicalFurnitureId) — единая точка входа без
исключений, подтверждённая и чтением, и мутационным тестом, и живым
прогоном браузерного смока и golden-кадра. Все шесть AC закрыты и
перепроверены самостоятельно, а не приняты на веру.
Вердикт: зелёный.
Материал раунда
- Ветка:
issue/606-furniture-catalog-corrections, коммит6d6d6a131778— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
ce1a14e2528cab1fefbe745d0711ba93aa0b6a7egit log --all --format='%H %T' | grep ce1a14e2528c - Тело issue:
96bd494bbf2f992639dc27711a166b327427c83505722e10119c467c66dbbfd5 - Вердикт конвейера:
green· High 0