Files
houseplan-card/docs/reviews/CODE-REVIEW-140-r1.md
T
2026-08-14 16:08:32 +03:00

16 KiB
Raw Blame History

CODE-REVIEW-140-r1

  • Issue: #140 — узкий футер диалогов свойств проёма и физического объекта
  • Трек: trivial (короткий трек, §5.1 PROCESS.md)
  • Коммит: 40550dc618b5df59a1e123008d7207a899179d4b "Fix editor property dialog footer width", ветка issue/140-dialog-footer-width
  • Диапазон: origin/dev..HEAD = один коммит
  • Ревьюер: Claude (код-ревью), сессия без контекста реализации

Скоуп

Диапазон git diff origin/dev...HEAD затрагивает 8 файлов:

  • src/houseplan-card.ts — добавлен атрибут wide на <hp-dialog> в _renderOpeningDialog() (:16431) и _renderPhysicalDialog() (:16745). Больше ничего в файле не менялось.
  • test/golden-matrix.test.mjs — существующий unit-тест «all destructive editor dialogs use the shared responsive footer groups» переименован и расширен проверкой assert.match(openTag, /\bwide\b/, …) для _renderOpeningDialog, _renderPhysicalDialog, _renderSpaceDialog.
  • demo/smoke_dialog_footer_width.mjs — новый browser-smoke (215 строк), измеряет реальную геометрию футера в обеих локалях на десктопной и узкой ширине.
  • docs/CHANGELOG.md, docs/CHANGELOG.ru.md — по одному пункту, со ссылкой на #140.
  • dist/houseplan-card.js, custom_components/houseplan/frontend/houseplan-card.js, demo/srv/assets/houseplan-card.js — три копии сгенерированного бандла, идентичные друг другу (см. ниже).

src/styles.ts (общий dialog-action-footer/перенос) не тронут — это соответствует явному «не в скоупе» из решения владельца. Диалог пространства (_renderSpaceDialog, :18042) не менялся: он уже использовал wide.

Изменение соответствует зафиксированному владельцем решению в комментариях к issue (два уточнения: перенос дефекта на hp-dialog.ts/атрибут wide, затем включение диалога физических объектов в тот же скоуп) и не расширяет его — CSS-контракт переноса футера не тронут, третья ширина не введена, диалог черновика (4-кнопочный вариант того же _renderPhysicalDialog) не получил требования «в одну строку», только требование не переполняться.

Продуктовая линия по docs/SCOPE.md: правка полирует J6 («Keep the plan true as the home evolves» — два редактора, повседневная работа админа дома в десктопном браузере) — чисто визуальный дефект layout в admin-only поверхности, не открывает новых возможностей и не противоречит инвариантам View mode.

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

Соразмерность гейтов: диапазон меняет два вызова компонента и добавляет один таргетный smoke; полный smoke/golden/performance/pytest не запускался — обоснование в разделе «Чего не проверял».

Гейт Команда Результат
Typecheck npx tsc --noEmit green, без вывода
Unit npm test green, 789/789
Build + sync копий npm run build затем cmp трёх копий green; после локальной пересборки рабочее дерево осталось чистым (git status --short пусто) — закоммиченный бандл байт-в-байт воспроизводим
Bundle hash sha256sum dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js demo/srv/assets/houseplan-card.js все три 9b87823ed5b7b0346657c9cd630f61346adf51e0d228225f9f7baa49bc4d8171
Целевой smoke node demo/smoke_dialog_footer_width.mjs (после npm run build + копирования в demo/srv/assets) OK, все 24 проверки true — числа ниже

Числа из прогона smoke (десктоп 1000×820, узкий 320×820):

Диалог Язык surfaceWidth spareWidth (запас) oneRow
opening en 500.00 240.31 true
physical en 500.00 240.31 true
opening ru 500.00 187.09 true
physical ru 500.00 187.09 true
space (контроль) ru 500.00 — true, не регрессировал

Узкая ширина (320 px): opening/physical surfaceWidth = 300.8, вписан в 320*0.94+1 = 301.8, wrapped: true (перенос сохранён), noHorizontalOverflow: true. Четырёхкнопочный draft (сегмент черновика) — buttons: 4, buttonsContained: true, insideViewport: true, без требования одной строки, как и оговорено в issue.

Дисциплина «тест должен уметь падать» — проверена для обоих прогнанных тестов, не только предъявлена:

  1. Unit-тест test/golden-matrix.test.mjs (сабтест «all destructive editor dialogs use the medium shell…»): временно откатил wide в _renderOpeningDialog(), прогнал node --test test/golden-matrix.test.mjs → сабтест 13 упал (AssertionError: _renderOpeningDialog must reserve the existing 500 px desktop shell). Откатил правку, git status --short снова пуст.
  2. Smoke demo/smoke_dialog_footer_width.mjs: тем же способом откатил wide у _renderOpeningDialog(), пересобрал (npm run build, копия в demo/srv/assets), прогнал smoke → opening_en_medium_shell: false, opening_ru_medium_shell: false (остальные проверки, не зависящие от этого диалога, остались true, как и должно быть). Откатил правку, пересобрал, сверил хеш бандла — снова совпадает с закоммиченным.

Оба теста реагируют на регресс именно в той точке, которую призваны защищать; это не тавтологичные проверки.

AC — разбор

  • AC1 (три кнопки в одну строку в обоих диалогах, en/ru). Доказано smoke: ..._three_actions_one_row: true для всех четырёх комбинаций диалог×язык, buttons: 3 в каждом случае, oneRow: true. Соответствует требованию владельца «Удалить» слева / «Отмена», «Сохранить» справа — подтверждено по коду (dialog-action-danger содержит одну кнопку btn danger с _deletePhysicalSelection/аналог для проёма, dialog-action-commit — Cancel+Save) и геометрически (dangerRect/commitRect на одной строке через oneRow).
  • AC2 (запас положительный, перенос не срабатывает, числа названы). Доказано smoke: spareWidth — 240.31 px (en) и 187.09 px (ru) на обоих диалогах, positive_localization_headroom: true, oneRow: true — перенос не сработал ни в одной из измеренных комбинаций. Числа приведены выше и воспроизведены мной отдельным прогоном, а не переписаны из хендоффа.
  • AC3 (узкий экран без горизонтального скролла, перенос сохранён, диалог пространства и четырёхкнопочный футер черновика не регрессируют). Доказано smoke на 320 px: opening_narrow_fits_viewport, physical_narrow_fits_viewport, opening_narrow_keeps_responsive_wrap, physical_narrow_keeps_responsive_wrap, draft_four_actions_remain_contained, space_narrow_not_regressed — все true. wrapped: true подтверждает, что CSS-перенос (не тронутый диффом) по-прежнему срабатывает на узком экране, а не был случайно отключён атрибутом wide (обе ширины заданы через min(px, vw), как и предполагал владелец в комментарии, и это подтверждено измерением, а не одним лишь чтением кода).

Все три AC доказаны исполняемым smoke с реальными числами, а не «verified» без команды.

Проверено чтением, не исполнением

  • Диапазон правки в src/houseplan-card.ts ограничен ровно двумя строками (добавление токена wide к открывающему тегу <hp-dialog>), подтверждено постатейным чтением diff — никаких сопутствующих изменений в теле методов, в _deletePhysicalSelection, _deleteDraftSegment, _deleteDraftWhole или в обработчиках _saveMarker/_savePhysicalDialog нет.
  • src/hp-dialog.ts не менялся; свойство wide = false с reflect: true (:36, :220) и правило :host([wide]) .surface { width: min(500px, 94vw); } (:157) — уже существующий, семь раз используемый до этой правки контракт, что подтверждает заявление issue «не вводит нового UX-контракта».
  • src/styles.ts (dialog-action-footer, dialog-action-group, dialog-action-danger/commit, :2989–3012) не менялся — контракт переноса футера, о котором явно сказано «не менять», прочитан и подтверждён нетронутым по diffstat.
  • Диалог сегмента черновика (d.kind === 'draft', :16780) рендерится тем же _renderPhysicalDialog(), получил wide наравне с диалогом физического объекта — это не отдельная ветка кода, значит расширение ширины распространяется на него автоматически; узкий smoke подтверждает containment (4 кнопки), но не требует одной строки, что соответствует явному «не в скоупе» issue.
  • Трейлеры коммита: Issue: #140, User-Visible: yes — оба changelog правлены в этом же коммите (docs/CHANGELOG.md, docs/CHANGELOG.ru.md), формулировки используют терминологию docs/USER-GUIDE.ru.md («проём», «Удалить», «Отмена», «Сохранить»).
  • Ветка issue/140-dialog-footer-width соответствует имени issue; коммит один, без промежуточных «допишу потом».

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

  • Продуктовая правка минимальна и точна: два атрибута wide, использующих существующий, уже семикратно применяемый вариант ширины — ровно то решение, которое зафиксировал владелец.
  • CSS-контракт переноса футера и вторая (узкая) ширина диалога не тронуты — соответствует «не в скоупе».
  • Три копии бандла синхронны и воспроизводимы локальной пересборкой байт-в-байт (проверено дважды: до и после экспериментов с откатом).
  • Тесты (unit + smoke) реально проверяют регресс, а не тавтологичны — оба прожаты в «падающем» состоянии.
  • Трейлеры, changelog, формат хендоффа и именование ветки соответствуют PROCESS.md/AGENTS.md.
  • Автоматическое ревью до этого не отработало (см. комментарий issue, прогон); данный документ и вердикт — первый фактический код-ревью по этой задаче, цикл r1/2 (лимит короткого трека).

Чего не проверял — и почему

  • node demo/smoke_*.mjs (остальные 126 из 127) — не прогонял. Diff не трогает canvas-геометрию, солнце, LQI, освещение, изометрию, тач-инпут и прочие поверхности; единственная затронутая поверхность (футер двух диалогов свойств) покрыта специально написанным smoke_dialog_footer_width.mjs, который я прогнал и проверил на способность падать. Прогон всех 127 уместен «когда задача задевает всё» — здесь не тот случай (§8 PROCESS.md, issue #127).
  • npm run golden:verify — не прогонял. Просмотрел demo/golden/matrix.mjs целиком: ни один из golden-сценариев не открывает _renderOpeningDialog/_renderPhysicalDialog (там есть dialog: 'device', 'decor-color', 'backup-full', 'backup-space', но не свойства проёма или физического объекта) — набор физически не видит эту поверхность и не дал бы дополнительного сигнала. styles.ts (общая геометрия/цвета плана) не тронут, так что риска регресса в покрытых golden сценах тоже нет.
  • python -m pytest tests_backend -q — не прогонял. custom_components/**/*.py не входит в diff.
  • Performance-профили — не прогонял. Ни AC, ни diff не называют чувствительных к перфу путей; правка — статический HTML-атрибут в двух местах рендера диалога.
  • Скриншот в реальном браузере (визуальная приёмка человеком) — не делал; вместо этого — измерение геометрии через Playwright в smoke (тот же уровень автоматической проверки, который процесс признаёт доказательством).

Находки

Нет. High: 0, Medium: 0, Low: 0.

Вердикт

Все три AC доказаны исполняемым тестом с реальными числами, дисциплина «тест должен уметь падать» подтверждена для обоих прогнанных тестов, гейты по таблице выше зелёные, трейлеры и changelog в порядке, скоуп не расширен и не сужен относительно решения владельца.

Вердикт: зелёный · цикл r1/2 · High: 0 · Medium: 0 → нет новых issue