21 KiB
CODE-REVIEW-505-r1
Issue: #505
Материал: origin/dev..HEAD, диапазон 2e387ac1..d32dfc9b, 7 коммитов, рабочая
копия на d32dfc9be107dce72a2fa465413633caf717d1f5 (проверено git status —
чисто, HEAD == материал).
ТЗ: docs/specs/505-summary-panel-design-parity.md,
принято зелёным на S4 r2 (SPEC-REVIEW-505-r2).
Заход code-review: r1 (первый код-ревью этой задачи; предыдущие r1/r2 в
истории коммитов относились к этапу spec, не к этому этапу) — разбор полный.
Скоуп
Задача переводит сводную панель (#437/#493, эксплуатационное исключение
docs/SCOPE.md) на дизайнерский прототип (архив макет.zip) плюс четыре
исходных UX-исправления: плавное скрытие панели, широкий диалог настроек,
удаление Sizes on this screen, зависимость mobile-опции от master-toggle.
Изменения — три поверхности: составная кнопка в шапке/киоске, плавающая
панель, диалог настроек. Данные, источники, backend, geometry и public
config schema не затрагиваются (подтверждено чтением диффа — правки только
в src/summary-panel-*.ts, src/houseplan-card.ts (порядок кнопок),
i18n, docs, тесты/смоки/mutation-gate).
Как проверялось
Материал не переключался (git fetch/pull/checkout не выполнялись), рабочая
копия уже была на d32dfc9b.
Прочитан весь дифф git diff origin/dev...HEAD построчно: все хунки
src/summary-panel-runtime-loaded.ts, summary-panel-presentation.ts
(новый файл, ключевой для AC3/AC4), summary-panel-editor.ts,
summary-panel-editor-style.ts, summary-panel-dialog-style.ts,
summary-panel-style.ts, summary-panel-icons.ts (новый), summary-panel-i18n.ts,
houseplan-card.ts, THIRD_PARTY_NOTICES.md, оба CHANGELOG, оба
USER-GUIDE, ARCHITECTURE.md, STATUS.md, test/summary-panel-presentation.test.mjs
(новый, 465 строк), test/summary-panel.test.mjs, scripts/mutation-gate.mjs,
scripts/check-inputs.mjs, scripts/smoke-links.mjs.
Гейты — свои, зелёного Validate на этом SHA нет:
npx tsc --noEmit— PASS, чисто.npm test— PASS, 2400/2400 (1 skipped — известный backend-only тест без HA harness), 40s.npm run build— PASS.npm run bundle:sync— PASS;git statusпосле — чисто (три копии бандла byte-for-byte совпали без новых диффов).npm run bundle:budget— initial View 298558 B gzip, в бюджете (потолок 301066, headroom 2508 — совпадает с заявленным автором +1B к базе; низкий headroom — известный системный долг #367/#474, не новый в этой задаче).node scripts/check-docs.mjs --screenshots=warn— PASS, только ожидаемый WARN о устаревшем скриншот-отпечатке (не ошибка вне release candidate).node scripts/check-inputs.mjs --coverage— PASS (exit 0).node scripts/mutation-gate.mjs --check— PASS, все guard'ы валидны формально.node scripts/process-gate.mjs --range origin/dev..HEAD— гейт пройден; 1 WARN о классификацииTHIRD_PARTY_NOTICES.mdвне таблицы классов — это preexisting-пробел классификатора (файл существовал до задачи в обеих копиях, изменение — лишь дописанная секция), не новое нарушение классов A/B/C/D.
Все шесть новых mutation-gate witness'ов прогнаны лично по одному
(node scripts/mutation-gate.mjs --id=<id>), не поверх --check:
summary-hide-unmounts-before-animation, summary-dialog-loses-wide-shell,
summary-mobile-ignores-local-off (все три через чистый+мутантный прогон
smoke_summary_panel_polish.mjs), summary-animation-stale-completion-unguarded,
summary-animation-reset-keeps-presentation, summary-animation-ignores-reduced-motion
(все три через целевые юнит-тесты summary-panel-presentation.test.mjs).
Для каждого: чистый прогон зелёный, мутант красный, «поймано 1 из 1», exit0 —
дисциплина «тест умеет падать» выполнена лично, не со слов автора.
Браузерные смоки — выбор по node scripts/smoke-select.mjs --base origin/dev --head HEAD:
прямое совпадение — smoke_summary_panel.mjs; 32 слабые связи только по
общему имени _mode, из которых relevant (в теме диалогов/навигации/kiosk/lazy)
восемь уже прогнаны автором, остальные — geometry/PDF/isometric/sun/walls,
не относящиеся к диффу (диалоговая и presentation-логика геометрию не трогает).
Лично (не со слов автора) прогнаны:
smoke_summary_panel.mjs, smoke_kiosk.mjs, smoke_nav_persist.mjs,
smoke_dialog_footer_width.mjs, smoke_lazy_editor_chunk.mjs,
smoke_warm_dialogs.mjs, smoke_houseplan_panel.mjs — все PASS (полный JSON
осмотрен, включая pickerWitnessHas200RowsAnd10000States, lostAckClosesAsSuccess,
trueConflictStaysOpen для AC9). smoke_summary_panel_polish.mjs лично не
запускался отдельным прогоном, но его чистая версия трижды прогнана как часть
каждого DOM/presentation mutation-gate witness выше — те же ассерты, тот же
результат. Остальные 24 слабые связи (geometry/PDF/isometric/walls/sun) не
прогонялись: диффу они не сопутствуют ни по файлам, ни по описанному
поведению — только имя _mode их связывает.
Инварианты модели (npm run invariants) не прогонялись — дифф не касается
рёбер комнат, толщины стен, layout, marker.space, open_spans.
golden:verify не прогонялся — задача явно размечена как diagnostic-evidence,
не изменение golden/docs baseline (ТЗ §8); визуальная приёмка велась через
собственный портативный harness задачи, отдельно от golden. pytest tests_backend
не прогонялся — Python/backend не тронут.
Независимая визуальная проверка (не со слов автора)
Запущен node demo/capture_summary_panel_505.mjs --probe-only — подтверждено:
sourceSha в отчёте == d32dfc9b (материал ревью, не что-то другое),
0 page errors/external requests/websocket attempts, реальный pinned
home-assistant-frontend==20260729.7 (сверен SHA-256 из README) отдаёт
контракт ширины именно как в ТЗ §6: small=320, medium=580,
inherited(--ha-dialog-width-md:920px)=920, mobile=390 с radius:0 (реальный
HA fullscreen). Это и есть самое рискованное предположение ТЗ («предполагается
наследование --ha-dialog-width-md; проверяется на настоящем компоненте») —
подтверждено на подлинном компоненте, не на заглушке.
Затем запущен точечный --only desktop-light,settings-real-ha-desktop-light
(тот же исходный SHA, тот же report.json). Осмотрены лично PNG:
- панель (
desktop-light-product-panel.pngvs-reference-panel.png) — шапка, карточка блока, разделители строк, левая подпись/правое жирное значение совпадают по составу и иерархии с адаптированной палитрой темы; - составная кнопка (
-product-control.pngvs-reference-control.png) — шестерёнка слева, подсвеченная правая половина, разделитель — совпадает; - диалог настроек в подлинном HA (
settings-real-ha-desktop-light-product-dialog.pngvs-reference-dialog.png) — «Основные настройки»/«Блоки» карточки, счётчик «1 из 10», grip/стрелки/имя/глаз в шапке блока, ряды подпись/источник/удаление, пунктирная «Добавить значение», футер Отмена/Сохранить — состав и порядок совпадают с прототипом; ширина реально 920px в подлинном компоненте (не заглушке); кнопка «Удалить блок» видна частично на нижнем крае — это нормальная вертикальная прокрутка тела при зафиксированном футере (ТЗ §6), не обрезка.
Это независимое подтверждение AC1, AC2, AC5, AC6, а не пересказ отчёта автора.
Находки
Low — L1: неиспользуемый ключ локализации summary.block_visible
src/summary-panel-i18n.ts:55,124,193,262 — ключ summary.block_visible
("Visible"/"Показывать"/"Sichtbar"/"Visible") остался во всех четырёх
словарях. До этой задачи он обслуживал текстовую подпись чекбокса видимости
блока; в этой задаче чекбокс заменён на eye/eye-off кнопку с новыми ключами
summary.hide_block/summary.show_block (src/summary-panel-editor.ts,
секция summary-visibility). Проверено grep -rn "block_visible" src/ demo/ test/ docs/ scripts/ — единственные совпадения это сами объявления ключа,
потребителей в коде не осталось.
Не блокирует: сам по себе ключ не ломает функциональность и не виден
пользователю (мёртвая строка в бандле лишь занимает место). Тот же класс
дефекта, что явно требовалось устранить для summary.sizes_title (ТЗ §6:
«Remove summary.sizes_title only if no remaining consumer») — здесь тот же
принцип применяется правильно к одному ключу и упущен для другого. Автор
чинит в рамках этой задачи (Low: правится или снимается запиской) —
отдельный issue не заводится (only Medium outside scope получает issue,
здесь Low и в рамках задачи).
Что проверено и корректно
- AC1 (кнопки/порядок/состояния):
houseplan-card.tsпереноситrenderControls(false)в конец группы действий View (было — перед zoom-контролами). Иконки — новые inline SVGsummaryIcon()с Tabler-путями из прототипа, атрибуция вTHIRD_PARTY_NOTICES.md(оба места, root иcustom_components/).aria-hidden,focusable="false", явныйviewBox,currentColorчерезstroke="currentColor"— соответствует ТЗ §2/§3. Визуально подтверждено (см. выше). - AC2/AC6 (панель/диалог по макету): структура HTML и CSS
(
summary-panel-style.ts,summary-panel-editor-style.ts,summary-panel-dialog-style.ts) вводит.summary-scroll,.summary-block,.summary-general/.summary-blocks-card, grip/order/eye кнопки,summary-sourceс меткой и деталью источника, dashed add-строки, раздельный destructive footer блока. Селекторы либо scoped подhp-dialog[data-kind='summary']/:host([data-kind='summary']), либо используют классы с префиксомsummary-;hp-dialog.tsвообще не изменён (0 диффа) — совместное поведение других диалогов физически не затронуто. Визуально подтверждено. - AC3/AC4 (анимация lifecycle):
SummaryPanelPresentation— новый класс, единственный источник transient-состояния показ/скрытие, никогда не пишетlocal.show. Прочитан построчно: генерация (generation) инвалидирует устаревшиеfinished/timeoutколбэки;settle()— единственная точка отключения;sync(..., immediate)обрабатывает смену стороны и «жёсткие» границы синхроннымsettle; safety-timeout 250мс (≤300мс из ТЗ); reduced motion проверяется до создания WAAPI-анимации. Все шесть mutation witness'ов лично подтверждены (см. «Как проверялось»). Юнит-тестtest/summary-panel-presentation.test.mjs(465 строк) покрывает реверсы, document hidden/visible без replay, resize/anchor/eligibility границы, всеhardBoundaries(editor entry, disconnect, identity/route/kiosk/permission change) — читал построчно, сценарии совпадают с явным перечнем ТЗ §4. - AC5 (широкий диалог):
--hp-dialog-wide-width/--ha-dialog-width-mdвыставлены в 920px, scoped наhp-dialog[data-kind='summary'](summary-panel-editor-style.ts:4-5), наследуются как CSS custom properties в shadow-деревоhp-dialog/ha-dialog. Подтверждено на подлинном pinned HA-компоненте (см. выше) — это была явно поименованная в ТЗ как предположение, требующее проверки, точка риска. - AC7 (Sizes on this screen):
sizeRows/summary-local-sizes/summary-icon-scale/summary-font-scale/summary-size-resetполностью удалены изsummary-panel-editor.ts;context.saveLocalдляicon_scale/font_scaleиз формы больше не вызывается.kiosk.icon_scale/kiosk.font_scale/gs.resetне удалены из словарей — верно, у них остаются потребители вhouseplan-card.ts/houseplan-editor-runtime.ts(View/kiosk-масштаб отдельно).summary.sizes_titleвычищен из всех 4 словарей, потребителей не осталось (grep пуст). - AC8 (mobile зависит от master):
summary-mobile-show?disabled=${!dialog.localShow || dialog.busy}— ровно формула ТЗ §5; значение не обнуляется при выключении (mutation witnesssummary-mobile-ignores-local-offлично подтверждён). - AC11 (документация): оба CHANGELOG правлены в том же коммите
(
05778a47,User-Visible: yes), USER-GUIDE.md/.ru.md, ARCHITECTURE.md, STATUS.md актуализированы и не противоречат коду. Трейлеры всех 7 коммитов корректны (Issue: #505на каждом,User-Visibleсоответствует содержимому). - Инфраструктура ревью:
demo/helpers/README-ha-dialog-505.mdописывает реальные границы безопасности (loopback-only, CSP, request allowlist, запрещённый WebSocket) — лично подтверждено report.json (pageErrors: [],externalRequests: [],websocketAttempts: []), утверждения не голословны.
Чего не проверял
npm run invariants— дифф не касается геометрии/толщины/layout/marker.space.npm run golden:verify, полныйdemo/smoke_*(235 смоков целиком),performance_smoke,python -m pytest tests_backend— вне соразмерного объёма для этой задачи (см. обоснование выше); полный набор — обязанность pre-release гейта, не код-ревью.- 24 из 32 «слабых» smoke-select связей (geometry/PDF/isometric/walls/sun) —
связаны только общим именем
_mode, дифф их не касается ни файлами, ни описанным поведением. - Полный список визуальных пар из ТЗ §8 (все 5 контекстов панели ×
light/dark/bottom/kiosk/mobile, узкие 320/390, увеличенный текст,
длинные DE-строки) — лично осмотрены только 2 пары (панель+control light
desktop, реальный HA settings light desktop) как точечная проверка самых
рискованных утверждений (реальная ширина HA-компонента, состав диалога);
остальные пары — по отчёту автора (
ACCEPTANCE.md), не переснимались из-за объёма (полный прогон — десятки PNG и повторные загрузки/рендеры). Инструментарий (capture_summary_panel_505.mjs) лично проверен рабочим и привязанным к материалу ревью (sourceShaв report.json совпадает с SHA этого раунда), это снижает, но не снимает риск непросмотренных пар. demo/smoke_summary_panel_polish.mjsкак отдельный самостоятельный прогон (только через mutation-gate witness'ы, где он прогоняется чистым трижды с идентичным результатом) — не запускался напрямую отдельной командой.
Вердикт
High: 0 · Medium: 0 · Low: 1 (в скоупе, не блокирует). Все 11 AC доказаны либо автотестом с личной проверкой «тест умеет падать» (AC3, AC4, AC5, AC8 — mutation witness), либо смоками с реальными DOM/geometry ассертами (AC1, AC2, AC6, AC7, AC9), либо документами и трейлерами (AC11), плюс независимая визуальная проверка на подлинном HA-компоненте для самого рискового пункта (AC5). Зелёный вердикт.
Материал раунда
- Ветка:
issue/505-summary-panel-polish, коммитd32dfc9be107— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
147b26d3d229df542d072881387fd4f0f5650be1git log --all --format='%H %T' | grep 147b26d3d229 - ТЗ
docs/specs/505-summary-panel-design-parity.md, блобfed71b61cb26deae64fef8dc1a364d3401242d48git log --all --find-object=fed71b61cb26deae64fef8dc1a364d3401242d48 -- docs/specs/505-summary-panel-design-parity.md - Вердикт конвейера:
green· High 0