21 KiB
CODE-REVIEW-361-r2
Issue: #361 — «Мебель: физическая толщина линий не масштабируется при zoom»
Этап: code · заход r2 · блокирующих циклов израсходовано 1/4 (зелёные вердикты бюджет не тратят, #227)
ТЗ: docs/specs/361-furniture-stroke-zoom.md (зелёный в SPEC-REVIEW-361-r2, SHA 31c6e77c)
Раунд r1 код-ревью: жёлтый, SHA 4b273e94 (документ docs/reviews/CODE-REVIEW-361-r1.md,
единственная Medium-находка: golden-бейзлайны с реальной мебелью не приняты под новый
физический stroke).
Ветка приведена к dev конвейером до этого раунда: поверх легло 3 dev-коммита
(8896d610 -> 737d2f66, добавлены c50d9e42, 631bafea, 00197a3a, 23d6681d,
cbf9318e, f2e36572, 7c45372c — параллельные задачи #369/#366/#39, не #361). По §7.2
это другой код, и разбор ниже — полный, а не по дельте, несмотря на то что это
формально второй код-ревью-цикл.
Скоуп проверки
Диапазон origin/dev...HEAD, 46 файлов. Продуктовый код — тот же, что уже был полностью
разобран в r1: src/furniture.ts (+55/-4), src/houseplan-card.ts (+30/-6), коммит
6c608485 (fix: scale furniture strokes with plan zoom, trailers Issue: #361 /
User-Visible: yes). Я построчно сверил оба файла с диффом, процитированным в
CODE-REVIEW-361-r1.md, — содержимое побайтово идентично тому, что уже прошло полный
разбор на 4b273e94; сменился только SHA из-за двух последовательных ребейзов (сначала
ручной автором на dev@23d6681d после конфликта в generated bundle, затем конвейерный
перед этим ревью). Остальное — тесты, три копии бандла, документация, changelog,
docs-скриншоты и golden-бейзлайны.
Продуктовое рассуждение (сверено заново, не по памяти r1): задача закрывает строку J6
docs/SCOPE.md («Keep the plan true as the home evolves» — редактирование/drag/resize
существующих элементов плана должно оставаться физически достоверным) и косвенно J4
(создание плана «как физического объекта»). Диагноз (line/rect/ellipse получают физический
stroke-width без vector-effect, мебель — тот же stroke-width, но с принудительным
non-scaling-stroke, который одновременно гасит и локальный анизотропный transform, и
внешний camera zoom) подтверждён чтением кода заново, не переносится как готовый вывод.
Зелёного Validate на 737d2f66 нет — все гейты ниже прогнаны мной локально.
Как проверялось
- Прочитаны заново: весь diff
src/furniture.ts/src/houseplan-card.ts, ТЗ, issue и все комментарии обоих SPEC-REVIEW и обоих CODE-REVIEW раундов,docs/SCOPE.md(J4/J6),docs/FURNITURE.md,docs/USER-GUIDE.ru.md. - Построчное сравнение текущего диффа
src/furniture.ts/src/houseplan-card.tsпротив origin/dev с кодом, процитированным вCODE-REVIEW-361-r1.md:furniturePlanScreenScale,furnitureStrokePx, точки вызова в_renderFurniturePlacementPreviewи_renderDecorLayer— идентичны знак в знак. - Дешёвые гейты (прогнаны сам, Validate на этом SHA не найден):
npx tsc --noEmit— PASS;npm test— 1533 pass, 1 skip, 0 fail (~26.5s, полный набор);npm run build— чисто,git status --shortпуст после сборки;npm run bundle:sync— три копии бандла (dist/,custom_components/houseplan/frontend,demo/srv/assets) пересобраны,git status --shortпосле — пуст, дифф с рабочим деревом нулевой;npm run bundle:budget— PASS, initial View 273551 B gzip / budget 282000 B, запас 8449 B (запас чуть меньше, чем в r1 (9152 B), — за счёт трёх посторонних dev- коммитов, легших поверх при ребейзе, не этой задачи);node scripts/check-docs.mjs(diff трогаетsrc/**) — PASS, 7 файлов, 10 внешних ссылок;node scripts/no-new-any.mjs --base origin/dev --head HEAD— PASS, 75 новых строк в 2 файлах, новыхanyнет.
node scripts/smoke-select.mjs --base origin/dev --head HEAD— та же картина, что в r1: 29 «прямых совпадений» по_baseVb/_viewOr/_stageEl(методы камеры/viewport, не изменены диффом, только читаются в новой ветке рендера мебели) и 1 «слабая связь» (smoke_canvas_frame.mjs←_baseVb). Решение по каждой строке — как в r1: методы не изменены, ни pan/zoom/backdrop/kiosk/жесты/виртуальный свет к furniture-stroke отношения не имеют — не прогонялись;smoke_canvas_frame.mjs(слабая связь) просмотрен — про рамку вида/aspect, мебели не касается, не прогонялся.- Целевой смок из AC1/AC3/AC4/AC6:
node demo/smoke_furniture.mjs→ OK, все 90 полейtrue, включая растровые (furnitureFollowsPhysicalCameraZoom,designerAndPrimitiveMatchOrdinaryDecor,anisotropicResizeKeepsBothAxesEqual,rotatedArtworkKeepsTheSameThickness,viewportResizeRecalculatesTheSharedPhysicalStroke,previewAndCommitShareThePhysicalStroke). - Дисциплина «тест должен уметь падать» — унаследована из r1, не переисполнена: revert-
проверка (откат
src/furniture.ts/src/houseplan-card.tsкorigin/dev, повторный прогонfurniture-stroke-contract.test.mjsиdemo/smoke_furniture.mjs, восстановление дерева) уже документирована вCODE-REVIEW-361-r1.mdна побайтово идентичном коде. Гонять заново дорогой Chromium-revert-цикл ради кода, который я построчно сверил как идентичный, — трата времени без нового знания; см. раздел «Унаследовано из r1». - Golden baseline — находка r1. Проверил закрытие на факт-коде:
git diff origin/dev...HEAD -- demo/golden/baselines/baselines-index.jsonпоказывает, что изменены ровно 4 записи из ТЗ (furniture-placement-preview-light,furniture-plan-art-dark,decor-over-opaque-hover-light,decor-over-glow-base-dark) плюсsourceFingerprint/acceptedAt/witnesses.count(133→131, floor 10 — запас есть). Остальные 138 хешей не тронуты;- после
npm run bundle:sync(чтобыdemo/srv/assetsбыл собран из факт-кода) прогналnode demo/golden/run.mjs --mode=capture --scenario=<id>для всех 4 сценариев →passedна всех четырёх (актуальный рендер совпадает с новым принятым бейзлайном); - контрольный прогон
furniture-categories-light(мебель есть только в палитре, decor-слоя с реальной мебелью нет) далdifferent(diffRatio≈0.00175при пороге0.0008); чтобы отличить реальную регрессию от дрейфа среды, откатилsrc/furniture.ts/src/houseplan-card.tsкorigin/dev, пересобрал (bundle:sync) и повторил тот же сценарий — тот жеactualSha256, тот жеdiffRatio, то есть расхождение существует независимо от этого фикса (предсуществующий дрейф рендеринга, не тронутый диффом) — не находка. После проверки восстановил рабочее дерево (cpоригиналов +git reset HEAD,git diff HEADпуст) и пересобрал бандл; npm run golden:verifyцеликом (182 сценария) не запускался: инструмент сам запрещает--scenarioв режимеverify(«golden verify must run the complete matrix»), а diff не трогает ничего вне decor/furniture render — остальные сценарии не имеют мебели в decor-слое и не могут пострадать от этого diff (то же обоснование, что в r1).
npm run invariantsне прогонялся:git diff origin/dev...HEAD --stat -- src/показывает толькоsrc/furniture.tsиsrc/houseplan-card.ts, ни один не пишет и не читаетWallSegmentModel/рёбра комнат/layout/marker.space/open_spans— только decor/furniture render (перепроверено чтением обеих новых функций и их точек вызова).python -m pytest tests_backendне прогонялся: diff не трогаетcustom_components/**/*.py(только сгенерированные frontend-бандлы внутриcustom_components/houseplan/frontend/).- Performance-профили не прогонялись: AC8 не называет конкретный профиль; source-contract
тест (
test/furniture-stroke-contract.test.mjs) мутационно гарантирует, чтоfurnitureScreenScaleрезолвится один раз на весь decor-слой, не на предмет. - «Одно число — один источник»: физическая толщина мебели вычисляется один раз
(
furnitureScreenScaleв_renderDecorLayer) и передаётся параметром в_renderFurniturePlacementPreview— единственная точка полученияfurnitureStrokePx(...)для preview и единственная для saved-пути; второго независимого вычисления той же видимой величины нет (проверено чтением:houseplan-card.ts:8095-8098,8171, и мутационно —furniture-stroke-contract.test.mjsтребует ровно одно вхождениеconst furnitureScreenScale = furniturePlanScreenScale().test/single-source-numbers.test.mjsэту величину не называет явно, но входит в зелёныйnpm testи не задет диффом.
Закрытие раунда r1
| Находка r1 (Medium, в скоупе) | Чем закрыта | Где это видно |
|---|---|---|
Golden-бейзлайны 4 сценариев с реальной мебелью (furniture-plan-art-dark, furniture-placement-preview-light, decor-over-opaque-hover-light, decor-over-glow-base-dark) не обновлены под новый физический stroke; npm run golden:verify упал бы на SHA 4b273e94. |
Коммит 737d2f66 (test: accept furniture stroke golden baselines, trailers Issue: #361 / User-Visible: no / Baseline-Reviewed: <CI run 33240556186>) принимает ровно эти 4 хеша через npm run golden:accept -- --reviewed из полного Linux CI-артефакта; остальные 138 бейзлайнов не тронуты. |
git diff origin/dev...HEAD -- demo/golden/baselines/baselines-index.json — 4 изменённых записи; node demo/golden/run.mjs --mode=capture --scenario=<id> → passed на всех четырёх на факт-коде (раздел «Как проверялось», п.7). |
Других находок r1 не было (High: 0, Medium: 1 — закрыта выше).
Унаследовано из r1
Со ссылкой на docs/reviews/CODE-REVIEW-361-r1.md, SHA 4b273e94:
- Falsifiability-проверка новых тестов и смока (revert
src/furniture.ts/src/houseplan-card.tsкorigin/dev, повторный прогонtest/furniture-stroke-contract.test.mjsиdemo/smoke_furniture.mjs, наблюдение падения ровно на ожидаемых утверждениях, восстановление дерева) — не переисполнена в этом раунде. Основание: я построчно сверил текущийsrc/furniture.ts/src/houseplan-card.tsс кодом, процитированным в r1, — содержимое идентично; повторный дорогой Chromium-revert-цикл на неизменившемся коде не добавляет знания. - Разбор нетипичного iso-случая (
_baseVb()→!showBorders/no-borders ветка, гдеfurnitureScreenScaleсчитается отplanView, а фактический viewBox decor-слоя иногда берётся изfloorView) — переперечитан мной в этом раунде (те же строкиhouseplan-card.ts:11087-11090, тот же вывод: оба входа конечны и положительны,NaN/пропажа мебели невозможны, численная калибровка iso вне контракта #361) — формально не «унаследовано без проверки», а подтверждено заново, привожу здесь для полноты трассировки. - Продуктовый диагноз и AC1…AC9 как таковые (что чинить, что не входит в скоуп) —
установлены на этапе SPEC-REVIEW-361-r2 (SHA
31c6e77c, зелёный); в этом раунде я не оспаривал сам контракт, а проверял, что реализация ему соответствует.
Проверено и корректно (по AC)
- AC1/AC3 (физический zoom, rotate+zoom):
furniturePlanScreenScale— точная формула ТЗmin(viewportW/viewBoxW, viewportH/viewBoxH), учитывает letterboxing. Растровый смок подтверждает кратное изменение толщины при переходе zoom1→zoom2 для designer- и primitive- артворка, включая повёрнутый на 30°. - AC2 (анизотропный resize):
furnitureStrokePxне принимаетw/h/viewW/viewHпредмета вообще — анизотропии неоткуда взяться; смок подтверждает равенство толщины по осям в пределах допуска. - AC4 (preview/commit parity): один расчёт
furnitureScreenScaleна весь decor-слой, передан параметром в preview — единый источник для обоих render-путей; source-contract тест запрещает preview обходитьfurnitureStrokePx; смокpreviewAndCommitShareThePhysicalStroke: true. - AC5 (совместимость): полный
npm testзелёный (1533/1533, все 56 symbols, erase-hit,data-hp/data-kind/data-symbolне задеты); смок целиком (палитра, drag, resize, rotate, wall magnet, erase) зелёный. - AC6 (layout safety): юнит-тесты на нулевые/
NaN/Infinityвходы всегда возвращают конечное число; смокviewportResizeRecalculatesTheSharedPhysicalStrokeподтверждает пересчёт после ресайза viewport. - AC7 (поддерживаемые поверхности), кроме golden: рендер общий для View/kiosk/ Background editor, light/dark не входят в расчёт (проверено чтением); golden — закрыт выше отдельным разделом. Один нетиповой iso-случай разобран как явно принятый остаточный риск (раздел «Унаследовано из r1»), не находка.
- AC8 (perf/bundle): typecheck/test/build/bundle:sync/bundle:budget зелёные;
furnitureScreenScale— один вызов на слой, гарантировано мутационным тестом. - AC9 (документация):
docs/FURNITURE.mdбольше не приписываетnon-scaling-strokeфизическую толщину; оба changelog обновлены в том же коммите6c608485(User-Visible: yes);docs/USER-GUIDE.ru.md— одно согласованное с разделом предложение.
Чего не проверял
- Полный
npm run golden:verify(182 сценария) — режимverifyне допускает фильтр по сценарию, а прогнать все 182 ради diff, трогающего только decor/furniture render, — несоразмерная трата времени; прогнаны 4 названных в находке r1 сценария плюс 2 контрольных (furniture-categories-light,tray-narrow-tool-ru), с явным разбором «различие или дрейф среды» для контроля. - Falsifiability revert-check новых тестов/смока — не переисполнена, см. «Унаследовано из r1» с обоснованием.
- Полная browser-smoke матрица (201 файл) — прогнан целевой
smoke_furniture.mjs; связи, названныеscripts/smoke-select.mjs, разобраны построчно (раздел «Как проверялось», п.4). npm run invariants— diff не геометрический (обоснование в п.8).python -m pytest tests_backend— diff не трогаетcustom_components/**/*.py(п.9).- Performance-профили/бенчмарки — не названы в AC, риск закрыт чтением и мутационным тестом.
- Ручное тестирование в браузере человеком — не проводилось; весь цикл проверен автоматикой и чтением кода, как предписано процессом для код-ревью.
Вывод
Код и тесты в этом раунде — тот же, уже полностью разобранный в r1, продукт содержательно
не изменился, изменился только SHA из-за ребейза на ушедший вперёд dev. Единственная
Medium-находка r1 (непринятые golden-бейзлайны) закрыта точно предписанным в ТЗ способом:
4 названных сценария приняты из полного Linux CI-артефакта, passed подтверждён локальным
--mode=capture прогоном на факт-коде, контрольные сценарии показывают только
предсуществующий дрейф среды, не связанный с этим диффом. Новых находок нет.
High: 0 · Medium: 0 → в задаче