docs: review document for #647
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 13m31s
Проверка (CI) / Классификация изменённых файлов (push) Failing after 14m40s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Skipped
Проверка (CI) / HACS: валидация репозитория (push) Skipped
Проверка (CI) / Hassfest: манифест интеграции (push) Skipped
Проверка (CI) / Мутанты по диффу (1/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (2/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (3/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (4/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (5/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Мутанты по диффу (6/6): затронутые свидетели краснеют (push) Skipped
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Skipped
Проверка (CI) / Смоки в браузере (шард 1 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 2 из 3) (push) Skipped
Проверка (CI) / Смоки в браузере (шард 3 из 3) (push) Skipped
Проверка (CI) / Смоки: все шарды зелёные (push) Skipped
Проверка (CI) / Golden-кадры против принятых эталонов (push) Skipped
Проверка (CI) / Перф-смок: бюджет времени кадра (push) Skipped
Проверка (CI) / Геометрия: TS/Python parity исполнена (push) Skipped
Проверка (CI) / Бэкенд: pytest в Home Assistant (push) Skipped
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 1m38s

Issue: #647
User-Visible: no
This commit is contained in:
claude[bot]
2026-09-25 08:57:28 +00:00
parent f0ba7c5da9
commit d35a52a3f1
2 changed files with 211 additions and 1 deletions
+209
View File
@@ -0,0 +1,209 @@
# 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\`<button>...\` : 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 доказаны либо
воспроизведённым красным мутантом, либо чтением кода с объяснением
инварианта. Реализация закрывает ровно тот сценарий, который просил
владелец: число убрано, × стабилен по позиции и размеру на всех
проверяемых ширинах, сводная панель не задета.
**Зелёный.**
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/647-toolbar-stable-width`, коммит `f0ba7c5da974` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `1adfc8927e46176aa74764f46be70bd5e3ff8b08`
```
git log --all --format='%H %T' | grep 1adfc8927e46
```
- Тело issue: `96f3d23ef7e626083567418cc5d5c098150d97627da79dc8ebe77b949f39a1c1`
- Вердикт конвейера: `green` · High 0
+2 -1
View File
@@ -1,11 +1,12 @@
# Индекс ревью
Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1047, issue: 368. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`.
Генерируется `node scripts/reviews-index.mjs` (#635) — не редактировать руками. Документов: 1048, issue: 368. Вердикт: 🟢 зелёный · 🟡 жёлтый · 🔴 красный · ⚪ не распознан (свободная форма старых документов). H/M — число High/Medium по строке вердикта или заголовкам находок. Файлы — пути, названные в находках; ищите по имени файла: `grep form-kit INDEX.md`.
| Issue | Документ | Этап · раунд | Вердикт | H | M | Находки | Файлы |
|---|---|---|---|---:|---:|---|---|
| #647 | [SPEC-REVIEW-647-r1.md](SPEC-REVIEW-647-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | П.4 ТЗ переносит на новый слот прежнее; ТЗ не упоминает и не защищает документированный | `src/styles/dialogs.styles.ts` `src/styles.ts` `smoke_glow_blending.mjs` `smoke_test_facade.mjs` `smoke_unified_wall_tool.mjs` `docs/UX-MODES.md` `docs/reviews/CODE-REVIEW-195-r1.md` `src/styles/chrome.styles.ts` |
| #647 | [SPEC-REVIEW-647-r2.md](SPEC-REVIEW-647-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — |
| #647 | [CODE-REVIEW-647-r1.md](CODE-REVIEW-647-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #646 | [CODE-REVIEW-646-r1.md](CODE-REVIEW-646-r1.md) | code · r1 | 🟢 зелёный | 0 | 0 | — | — |
| #645 | [SPEC-REVIEW-645-r1.md](SPEC-REVIEW-645-r1.md) | spec · r1 | 🟡 жёлтый | 0 | 2 | Отсутствует обязательная строка Touch editor: … из docs/TOUCH-SUPPORT.md → «Documentati…; ТЗ не учитывает существующую зависимость live-подписей Resize от позиции кнопки «Настро…; / Low | `docs/TOUCH-SUPPORT.md` `docs/reviews/SPEC-REVIEW-449-r1.md` `docs/specs/359-furniture-placement-preview.md` `docs/specs/449-double-fit-all.md` `src/houseplan-editor-runtime.ts` `src/houseplan-card.ts` `src/resize-labels.ts` `docs/process/REVIEWER.md` |
| #645 | [SPEC-REVIEW-645-r2.md](SPEC-REVIEW-645-r2.md) | spec · r2 | 🟢 зелёный | 0 | 0 | — | — |