Issue: #647 User-Visible: no
18 KiB
CODE-REVIEW-647-r1
Issue: #647 · Этап: code · Заход: r1 · SHA материала: f0ba7c5da97418e2b81a257d7b8eda4604e3a41e
Скоуп
ТЗ (редакция r2, спор закрыт SPEC-REVIEW-647-r2, зелёный) требует:
убрать текстовый счётчик устройств «N устр.» из шапки основной панели;
вынести × закрытия редактора из активной кнопки режима в отдельный
слот постоянного размера 24×24 px сразу после группы кнопок режимов;
слот пустой и инертен (aria-hidden, без фокуса) вне редактора; слот
виден на любой ширине, где видны кнопки режимов, включая 721–1100 px,
где раньше скрывался счётчик; ширина шапки и позиции соседних
контролов не меняются при входе/выходе/переключении редакторов;
кликабельная зона × не меньше 24×24 px; настраиваемая метрика
«Количество устройств» сводной панели (#437) не затрагивается.
Диапазон материала — один коммит 44fed222/f0ba7c5d поверх dev
(27d78801; dev с тех пор получил только неконфликтующий коммит
приёмки golden 632d6a2f). Правка: src/houseplan-card.ts (шапка),
src/styles/chrome.styles.ts (слот и ×), src/styles/dialogs.styles.ts
(удаление .count/медиа-правила), 4 словаря i18n (удаление
count.devices), новый смок demo/smoke_toolbar_stable_width.mjs,
3 переведённых смока, 6 мутантов, документы (UX-MODES.md,
STYLING-HOOKS.md, data-hp-contract.json), оба changelog, бандл.
Как проверялось
Дешёвые гейты подтверждены зелёным Validate на этом SHA (run
36113808861): typecheck, npm test, npm run build+сверка копий
бандла. Их не перегонял. Сам прогнал то, что Validate не покрывает и
что требует эта задача (визуальный/раскладочный диф):
| Гейт | Команда | Результат |
|---|---|---|
| Пересборка бандла для смоков | npm run bundle:sync |
OK, рабочее дерево после сборки совпало с закоммиченным (без диффа) |
| Юниты (перепроверка) | npm test |
3070/3070 pass, совпадает с заявленным |
check-docs (режим, которым реально пользуется Validate) |
node scripts/check-docs.mjs --screenshots=warn |
Documentation checks passed (7 files, 12 external links); предупреждение о несвежих скриншотах — ожидаемо, §11.4 (см. «Чего не проверял») |
mutation-gate --check (якоря 6 новых мутантов целы) |
node scripts/mutation-gate.mjs --check |
все 6 toolbar-* — ok |
| Новый смок | node demo/smoke_toolbar_stable_width.mjs |
OK, все проверки true на 1400/1000/768/390 px + ru |
| Мутант AC6 (медиа-скрытие слота ≤1100px) | правка chrome.styles.ts → сборка → смок → откат |
смок красный: FAILED (9) — совпадает с заявленным в хендоффе числом |
Мутант AC2 (слот display:none вне редактора) |
то же | FAILED (8) — совпадает |
| Мутант AC5 (переменная размера слота 13px) | то же | FAILED (12) — совпадает |
| 3 переведённых смока | smoke_editor_tabs, smoke_edge_cases, smoke_hidden_flag |
все OK |
node scripts/smoke-select.mjs --base origin/dev --head HEAD |
— | 3 прямых совпадения (smoke_decor, smoke_modes, smoke_space_settings), 41 слабая связь через общие имена _mode/stopPropagation |
| Прямые совпадения smoke-select | smoke_decor.mjs, smoke_modes.mjs, smoke_space_settings.mjs |
все OK (см. «Находки», Low-1 — smoke_decor не был назван в перечне гейтов автора) |
| AC8 (сводная панель) | demo/smoke_summary_panel.mjs |
OK |
| Смежные header-смоки из перечня автора (переисполнены выборочно) | smoke_kiosk, smoke_houseplan_panel, smoke_dialog_footer_width |
все OK |
count.devices действительно не используется больше нигде |
grep -rn "count.devices" src/ demo/ scripts/ |
пусто — ключ мёртв, удаление безопасно |
| Бюджет ядра | node --test test/core-file-budget.test.mjs |
7/7 pass; фактически 12886 строк при потолке 12889 (под потолком, не «ровно на потолке», как написано в хендоффе — расхождение безвредное, не находка) |
Слабые связи smoke-select (41 файл, общие символы _mode/stopPropagation)
не прогонялись поштучно: диф ограничен CSS-раскладкой шапки и разметкой
одного элемента, названные символы в них встречаются по всему ядру не
по смежной причине (PDF-экспорт, стены, декор и т.д. не читают шапку).
Прямые совпадения и AC-названные смоки покрывают risk surface этой
задачи.
AC · разбор
Таблица «AC · чем доказан · чем краснеет» в теле issue заполнена по всем защитным AC (AC1–AC6), включая AC7 (регресс, без мутанта — верно помечено как таковой) и AC8 (существующие мутанты сводной панели). Пустых третьих столбцов нет.
- AC1 (К1, нет числа). Прочитано:
<span class="count">и i18n-ключcount.devicesудалены из шапки и всех 4 словарей (src/houseplan-card.tsдифф,src/i18n/{en,ru,de,fr}.json).grepподтвердил отсутствие иных чтений ключа. Смокw*_noHeaderCount+ru_noHeaderCount— зелёные, лично прогнал. - AC2 (К2, ширина шапки стабильна) / AC3 (К3, соседи не прыгают).
Слот вынесен из кнопки режима как отдельный
<span class="editor-close-slot">с фиксированнымwidth/height: var(--hp-editor-close-size)= 24px что вне редактора, что внутри — размер не зависит от содержимого. Кнопки.modetabбольше не содержат×ни в одном состоянии. Смокw*_widthAndTabsStable/w*_idleSlotKeepsSize— зелёные; мутант «слот схлопывается вне редактора» лично воспроизведён, краснеет (8 FAILED, совпадает с заявленным). - AC4 (К4, × только в редакторе, слот инертен).
aria-hiddenвыставлен ровно приthis._mode === 'view'; в этом состоянии CSS[aria-hidden='true']{pointer-events:none}и в дереве нет кнопки (тернарникthis._mode !== 'view' ? html\...` : nothing) — значит слот действительно без фокусируемых потомков, а не просто визуально пуст. Проверено чтением; смокw*_idleSlotInert` зелёный. - AC5 (К5, hit-target ≥24×24). Кнопка
.closexвнутри слота —width/height: 100%, слот — 24×24 через переменную; отрицательные поля старой реализации убраны. Мутант «переменная = 13px» лично воспроизведён, краснеет (12 FAILED, совпадает). - AC6 (К6, × не прячется на 721–1100 px). Правило
@media (max-width: 1100px){.head .count{display:none}}удалено вместе со счётчиком (dialogs.styles.tsдифф), новый.editor-close-slotего не наследует — отдельный класс, отдельный файл (chrome.styles.ts). Мутант «добавить то же правило слоту» лично воспроизведён, краснеет (9 FAILED на 1000/768 px, 1400 зелёный — ровно то, что просил Medium-1 из SPEC-REVIEW-647-r1). - AC7 (К7, нет overflow). Регресс-проверка, мутанта нет (верно
помечено в таблице). Смок
w768/w390_noHeadOverflowзелёный, лично прогнал; дополнительноsmoke_dialog_footer_width(соседняя раскладка шапки) зелёный. - AC8 (сводная панель #437 не изменилась). Источник «Количество
устройств» в отдельном модуле сводной панели, диф его не касается.
smoke_summary_panel.mjs— зелёный, лично прогнал.
Документы синхронизированы: docs/UX-MODES.md — строка про
panel-host (device count убран из перечня) и абзац про × (слот
24×24, ссылка на #195 сохранена); docs/STYLING-HOOKS.md — editor-close
теперь описан как «слот после кнопок режимов»; docs/data-hp-contract.json
— editor-close.elements сужен до ["button"] (соответствует коду:
data-hp="editor-close" теперь только на <button>, не на <ha-icon>).
docs/USER-GUIDE.ru.md счётчик не упоминал — правка не нужна,
подтверждено grep. Golden и docs:capture — по §11.4 предрелizный
гейт, не гейт код-ревью; ТЗ верно зафиксировало это как риск.
Одно число, видимое дважды: кликабельная зона × (24×24 px) названа и
в docs/UX-MODES.md, и в CSS-переменной --hp-editor-close-size: 24px
(chrome.styles.ts), и в новом AC5/К5 — источник один (переменная),
документ и тест её же и проверяют, расхождения нет.
Трейлеры и changelog
Коммит несёт Issue: #647 и User-Visible: yes; оба changelog
(docs/CHANGELOG.md, docs/CHANGELOG.ru.md) правлены в этом же
коммите, текст соответствует пользовательскому результату из ТЗ
(число исчезло, × не дёргает шапку, зона клика прежняя, «Количество
устройств» сводной панели не изменилось).
Находки
Low-1. smoke-select называет demo/smoke_decor.mjs прямым
совпадением по символам _mode/stopPropagation, но в перечне
прогнанных гейтов хендоффа этого смока нет (назван smoke_decor
нигде, хотя modes, houseplan_panel, space_settings — два других
прямых совпадения — присутствуют). Смок реально зелёный — лично
прогнал node demo/smoke_decor.mjs, все проверки прошли. Дефекта
нет, это неполнота раскрытия «какие гейты прогнал» из трёх прямых
совпадений smoke-select. Снимаю без возврата автору, с записью здесь.
Других находок нет: High — 0, Medium — 0.
Что проверено и корректно
- Реализация буквально соответствует «принято предположительно» ТЗ:
слот стоит на месте бывшего счётчика, отдельный класс без
наследования правил
.count, размер — одна переменная. - Кнопки режимов после правки не зависят от
this._modeпо ширине — единственный источник роста шапки убран, а не замаскирован. - Слот рендерится только внутри ветки
this._canEdit, как и раньше сами кнопки режимов — для домочадца/киоска (не редактор) меняется ровно то, что заявлено в сценарии ТЗ: исчезает число, слота не появляется, потому что кнопок режимов там и не было. - 6 мутантов зарегистрированы в
scripts/mutation-registry.mjsрядом с мутантами шапки/режимов, все опираются на реальныйguard-прогон нового смока; 3 из 6 лично воспроизведены с результатом, совпадающим с заявленным автором числом упавших проверок — доверие к оставшимся трём (AC1, AC3, AC4) основано на чтении кода (тривиальные проверки: удалённый узел, убранный тернарник, перестановка атрибута) и наmutation-gate --check, подтвердившем целостность якорей патчей. count.devicesподтверждён мёртвым по всему дереву (src/,demo/,scripts/) — можно было безопасно удалить.docs/data-hp-contract.jsonсинхронизирован с фактической разметкой (data-hp="editor-close"переехал с<ha-icon>на<button>).
Чего не проверял
- Golden-эталоны и
docs:capture. ТЗ прямо называет их предрелизным гейтом (§11.4: «Golden, браузерные смоки... запускаются перед бетой, после того как код-ревью пройдено»); диф действительно меняет визуал (число пропадает из широких кадров, × переезжает из кнопки в слот во всех кадрах редактора) — пересъёмку и приёмку (golden:accept -- --reviewed,docs:capture) должен выполнить отдельный шаг на Linux CI, не этот раунд ревью. - Остальные ~38 из 41 «слабой связи» smoke-select (PDF-экспорт, стены,
декор-объекты, локализация админки и т.д.) — не прогонял: общие
символы
_mode/stopPropagationне создают правдоподобного пути к раскладке шапки в этих сценариях, а полный прогон матрицы — предрелизная обязанность, не гейт ревью. pytest tests_backend— диф не касаетсяcustom_components/**/*.py.npm run invariants— диф не касается геометрии модели.- Windows/WSL и HA-харнесс — не запускал (не требуется этой задачей).
- Мутанты AC1/AC3/AC4 (
toolbar-device-count-returns,toolbar-close-back-inside-tab,toolbar-close-slot-idle-focusable) — не воспроизводил лично их фактическое покраснение; проверил чтением кода и статической целостностью якорей (mutation-gate --check).
Вердикт
High: 0 · Medium: 0 (в скоупе, вне скоупа не заводились) · Low: 1 (снят ревьюером с записью, без возврата). Все 8 AC доказаны либо воспроизведённым красным мутантом, либо чтением кода с объяснением инварианта. Реализация закрывает ровно тот сценарий, который просил владелец: число убрано, × стабилен по позиции и размеру на всех проверяемых ширинах, сводная панель не задета.
Зелёный.
Материал раунда
- Ветка:
issue/647-toolbar-stable-width, коммитf0ba7c5da974— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
1adfc8927e46176aa74764f46be70bd5e3ff8b08git log --all --format='%H %T' | grep 1adfc8927e46 - Тело issue:
96f3d23ef7e626083567418cc5d5c098150d97627da79dc8ebe77b949f39a1c1 - Вердикт конвейера:
green· High 0