Issue: #233 User-Visible: no
15 KiB
CODE-REVIEW — issue #233, заход r2
- Issue: #233 — «Резайз показывает внутренние размеры, а не осевые»
- ТЗ:
docs/specs/233-resize-inner-dimensions.md, принят зелёным на r2 (docs/reviews/SPEC-REVIEW-233-r2.md) - Предыдущий раунд кода: r1, вердикт жёлтый, документ
docs/reviews/CODE-REVIEW-233-r1.md(коммитbff47f5), проверенный коммитabfaae3— feat: measure resize labels between wall faces - Заход: r2 (код), блокирующих циклов израсходовано 1 из 4 (потрачено на r1; зелёный этого раунда цикл не тратит)
Скоуп ревью
Это возврат по находкам M1/M2 предыдущего раунда, не новый заход задачи. Дельта
локальна: git diff abfaae3..HEAD (= коммит 81f3868) трогает ровно три файла —
test/wall-thickness.test.mjs, scripts/mutation-gate.mjs и сам документ r1-ревью.
Продуктовый код (src/wall-thickness.ts, src/houseplan-card.ts) не тронут —
это подтверждено не только диффом, но и байт-в-байт совпадением бандла (см.
«Как проверялось», п.2). Никаких признаков «ребейз на ушедший вперёд dev»,
смены контракта поведения или новой подсистемы нет — origin/dev...HEAD
идентичен тому, что видел r1, плюс этот один коммит. Поэтому разбор сокращён
до дельты и наследования, а не полного прогона AC1–AC9.
Закрытие раунда r1
| Находка r1 | Чем закрыта | Где это видно |
|---|---|---|
M1 — тест AC6b (test/wall-thickness.test.mjs:1533-1552 на abfaae3) не создавал настоящий сплит-разрыв: setWallThickness писал толщину на весь осевой отрезок ребра, поэтому thicknessCmAt по целому ребру совпадал с профилем, и тест не отличал бы исправленный код от регресса к H2 |
Тест переписан на реальный сплит: setWallThickness([], [0,0], [0.6,0], 20, pitch) при ребре комнаты [0,0]–[1,0] |
test/wall-thickness.test.mjs:1533-1571 (коммит 81f3868). Явные assert'ы: thicknessCmAt(split, [0,0], [1,0], pitch) === 0 (это и есть H2), thicknessCmAt(split, [0,0], [0.6,0], pitch) === 20, ownEdgeOffsets(...)[0] === halfDepth (не 0), плюс assert.notEqual(viaProfile[0], thicknessCmAt(...)) — тест теперь явно фиксирует расхождение источников, а не совпадение |
M2 — scripts/mutation-gate.mjs (диф abfaae3) содержал три мутанта из четырёх, названных в принятом §11 ТЗ; inner-span-ignores-neighbour-thickness (страж AC2) отсутствовал |
Добавлена запись мутанта, патчащая cutAt в innerEdgeSpan (const o = Math.max(0, Number(offsets[edge]) || 0); → const o = own;), привязанная к тесту innerEdgeSpan measures between wall faces |
scripts/mutation-gate.mjs:111-125 (коммит 81f3868). Независимо прогнан: node scripts/mutation-gate.mjs --id=inner-span-ignores-neighbour-thickness → «поймано 1 из 1» |
Обе находки были про доказательную базу, не про поведение продукта (r1 это установил независимым исполнением сценария сплит-ребра вне репозитория) — и продуктовый код в этом раунде действительно не менялся, значит поведение, подтверждённое в r1, не могло сломаться этой правкой.
Как проверялось
- Построчно сверен диф
81f3868с рецептами правок из документа r1 (см. таблицу выше) — обе правки текстуально соответствуют тому, что r1 просил исправить, а не что-то похожее. - Проверено, что продуктовый код не менялся:
git diff abfaae3..HEAD -- src/пуст; послеnpm run buildвсе три копии бандла дают тот же md5, что заявляли и автор, и r1-ревью —5ffd65073d92c4845aecb1a4145f0948(dist/houseplan-card.js,demo/srv/assets/houseplan-card.js,custom_components/houseplan/frontend/houseplan-card.js). Это делает независимую повторную проверку поведения продукта (AC1–AC9) избыточной — она физически не могла измениться. - Гейты прогнаны заново на дереве
81f3868(не унаследованы из r1, так как код тестов/гейта изменился):npx tsc --noEmit— чисто;npx tsc -p tsconfig.test.json— чисто (это конфиг, которым фактически компилируются тесты и мутационный гейд);npm test— 1026/1026, включая переписанный тест M1;node scripts/mutation-gate.mjs --check— 88/88ok, exit 0 (было 87 на r1, теперь на один больше — учтён новый мутант M2);- адресный прогон всех четырёх мутантов задачи по одному, чтобы не
доверять агрегату:
--id=inner-span-ignores-neighbour-thickness→ поймано 1 из 1 (новый, M2);--id=inner-span-reads-whole-edge-thickness→ поймано 1 из 1 (переписанный тест M1 всё ещё ловит его, но теперь по правильной причине — падает конкретно на сплит-ребре, а не потому что путь через профиль не исполнился);--id=inner-span-shortens-a-passage→ поймано 1 из 1;--id=resize-labels-show-centreline→ поймано 1 из 1.
- Прочитан патч M2 в контексте реального
src/wall-thickness.ts:898(cutAt, внутриinnerEdgeSpan) — строкаfindмутанта текстуально совпадает с текущим кодом, мутация подставляет собственную толщинуownвместо толщины соседнего ребраoffsets[edge], что и есть штраф AC2 («сокращение считается только по своей толщине, соседи игнорируются»). Патч не «на что-то похожее», а именно на тот код, который правка защищает. - Проверено, что дифф
81f3868не трогаетdocs/CHANGELOG*.mdиdocs/USER-GUIDE*.md— ожидаемо, коммит несётIssue: #233,User-Visible: no(тестовый коммит класса B), changelog не требуется.
Находки
Нет новых находок. Обе находки r1 закрыты доказательно (см. таблицу выше и «Как проверялось», пп. 1, 3, 4), High: 0, Medium: 0.
Отдельно отмечаю: в предпоследнем комментарии автор сообщил про красный job
docs в validate.yml (несвежий sourceFingerprint в
docs/images/screenshots.json из-за слияния #234 без пересъёмки) и завёл под
это отдельный issue. Это не находка этого ревью — дефект не в ветке #233, не
в скоупе задачи, и уже обслуживается отдельно самим автором, значит нового
issue от ревьюера не требуется (правило дублирования наблюдений, §12: не
заводить второй issue на то же наблюдение).
Что проверено и корректно
- M1, M2 — закрыты, доказано исполнением, не только чтением коммит-сообщения (см. «Как проверялось»).
- Продуктовое поведение (AC1–AC9) — не могло измениться этой правкой: бандл байт-в-байт идентичен версии, которую r1 уже проверил чтением, исполнением гейтов и независимым ручным сценарием сплит-толщины (H2).
- Трейлеры
81f3868:Issue: #233,User-Visible: no— корректны для тест-only правки; changelog не трогается, как и должно быть приUser-Visible: no. - Формальный контракт §11 ТЗ (четыре мутационных стража) — теперь
выполнен полностью: все четыре присутствуют в
scripts/mutation-gate.mjsи каждый independently подтверждён «1 из 1».
Унаследовано из r1
Без повторной проверки в этом раунде принято из docs/reviews/CODE-REVIEW-233-r1.md
(документ на коммите bff47f5, проверенный коммит продукта abfaae3):
- AC1–AC5 — численно сверенные длины (285/385 см на 300×400 при стенах 15 см), диагональные рёбра, случай «стены толще комнаты» → 0.
- AC6a (H1, правило нуля для проёмов) —
if (!(own > 0)) return centre;идёт первым вinnerEdgeSpan, перепроверено математически на кейсе H1. - AC6b (H2, источник толщин) — механизм
ownEdgeOffsets→roomWallProfile→atomicPolyForRoomподтверждён независимым исполнением сценария сплит-ребра вне репозитория (не тем тестом, который чинит M1, — отдельным ручным прогоном r1). В этом раунде довеском стало то, что теперь и сам юнит-тест воспроизводит этот же сценарий (закрытие M1 выше). - AC7 — три подписи
_rszEdgeLabelsи габарит_rszScaleLabelsвнутренние; подтверждено прогономdemo/smoke_resize_inner_dimensions.mjs(OK) на деревеabfaae3. Не перепрогонялось в r2, так как бандл (см. п.2 выше) идентичен — прогон на неизменившемся артефакте не может дать другой результат. - AC8 — площадь не менялась (
innerContourForRoom+floorMinusBodies), подтверждено тем же смоком (dragAreaStillInner,frameAreaAgreesWithSize). - AC9 — трейлеры и changelog для продуктового коммита
abfaae3(Issue: #233,User-Visible: yes,docs/CHANGELOG.md/.ru.mdиdocs/USER-GUIDE*.mdправлены в нём же). - i18n — диф
abfaae3не трогаетsrc/i18n/*.json. - Golden — вне риска по построению (
_rszLiveживёт только на время жеста ресайза, обнуляется наpointerup); проверено r1 чтением, не исполнениемgolden:verify. - Регрессия толщины стен —
smoke_draw_wall_thickness.mjs,smoke_wall_thickness_transition.mjsзелёные на деревеabfaae3. - Touch — диф не трогает pointer-пути,
docs/TOUCH-SUPPORT.mdне затронут.
Все эти пункты наследуются с уверенностью, потому что предмет наследования — поведение бандла, а бандл в r2 не пересобран из другого исходника: он тот же файл, что и на момент r1-вывода (совпадение md5 подтверждено заново в этом раунде, не взято на веру).
Чего не проверял и почему
demo/smoke_resize_inner_dimensions.mjsи регрессионные смоки повторно — не перепрогонял: продуктовый код не менялся (бандл идентичен), результат прогона на неизменившемся артефакте не может отличаться от того, что уже зафиксировал r1. Перепрогон был бы тратой минут без шанса найти что-то новое.npm run golden:verify— не гонял, по той же причине плюс аргумент r1 (оверлей_rszLiveвне активного жеста не рендерится, статический golden его не видит); дифф этого раунда вообще не вsrc/**.python -m pytest tests_backend— не гонял, диф не трогаетcustom_components/**/*.py.- performance-профили — не гонял, не названы в AC, диф не в горячих путях рендера и вообще не в продуктовом коде.
demo/smoke_*.mjsвне названных в AC/§12 — не прогонял весь набор из 127: дифф ограничен тестовым файлом и мутационным гейтом, у которых нет браузерной поверхности вовсе.
Вердикт
Обе находки r1 (M1, M2) закрыты именно так, как требовал документ r1: тест теперь строит настоящий сплит-разрыв и различает исправленный код от регресса к H2, мутационный гейт получил четвёртый страж контракта §11 и независимо подтверждён «1 из 1» по каждому из четырёх мутантов задачи. Продуктовый код не менялся — бандл байт-в-байт идентичен версии, которую r1 уже проверил чтением, гейтами и ручным сценарием, так что AC1–AC9 наследуются без риска регрессии из этой правки. Новых находок нет.
Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0