From 6ac513caa09a8e583869879412aa3a0c67d04488 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <209825114+claude[bot]@users.noreply.github.com> Date: Wed, 9 Sep 2026 19:43:54 +0000 Subject: [PATCH] docs: review document for #508 Issue: #508 User-Visible: no --- docs/reviews/CODE-REVIEW-508-r1.md | 123 +++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 docs/reviews/CODE-REVIEW-508-r1.md diff --git a/docs/reviews/CODE-REVIEW-508-r1.md b/docs/reviews/CODE-REVIEW-508-r1.md new file mode 100644 index 00000000..0783e6c4 --- /dev/null +++ b/docs/reviews/CODE-REVIEW-508-r1.md @@ -0,0 +1,123 @@ +# CODE-REVIEW-508-r1 + +Issue: #508 — «Сводная панель: в диалоге настроек не работает прокрутка — колесом мыши и touch на мобильных» +Материал: `768e1281f9119b12d018b416372984ff17eae62d` (ветка `issue/508-summary-dialog-scroll`, ребейзнута конвейером на `dev`: `7e5d394a → 768e1281`, +2 коммита dev). +Заход: r1 · блокирующих циклов израсходовано 0 из 2. Ребейз довёл ветку до другого дерева (§7.2) → разбор полный. + +## Скоуп + +Один коммит, класс A (`src/hp-dialog.ts`, `src/summary-panel-editor.ts`) + класс B (новый смок, +контрактный юнит-тест, два мутанта) + класс C (`docs/ARCHITECTURE.md`, оба CHANGELOG) + класс D +(пересобранный бандл во всех трёх копиях, `docs/images/screenshots.json` — только фингерпринт/`sourceSha256`, +`imageSha256` не изменились, что соответствует заявленному «11/11 попиксельно»). + +Правка ровно та, что описана в ТЗ (docs зафиксированы в теле issue, `SPEC-REVIEW-508-r1.md` → +`SPEC-REVIEW-508-r2.md`, вердикт зелёный): `hp-dialog` получает opt-in булев атрибут `flex-content` +(`flexcontent` на `ha-dialog`, обе HA-ветки рендера — с `described-by` и без), диалог настроек сводной +панели его объявляет. Нативная ветка (``) не тронута ни строкой. + +**J1** (`docs/SCOPE.md`) — обзорный диалог сводной панели входит в «show the whole home at a glance»; +это чистый баг-фикс регресса после #505, никакой новой функциональности и расширения скоупа нет. + +## Как проверялось + +Дешёвые гейты на этом SHA уже подтверждены Validate +(https://github.com/Matysh/houseplan-card/actions/runs/34395116095, `headSha=768e1281`, job +«Фронтенд: типы, юниты, мутанты, синхрон бандла» — success). Тем не менее часть из них прогнана +повторно вживую, потому что дёшево и диф трогает `src/**` (общий компонент `hp-dialog`, используемый +девятью другими вызывающими). + +| Гейт | Прогнал | Результат | +|---|---|---| +| `npx tsc --noEmit` + `rollup` (`npm run bundle:sync`) | да | сборка чистая; пересобранный `dist/` и обе копии (`custom_components/.../frontend`, `demo/srv/assets`) байт-в-байт совпали с закоммиченными — `git status` после пересборки пуст | +| `npm test` | да | 2437 тестов, 2436 pass, 1 skip (известный `smoke_opening_measure` не запускался тут — не относится к юнитам), 0 fail | +| `node scripts/no-new-any.mjs --base origin/dev --head HEAD` | да | «Новых any нет» (10 добавленных строк в 2 файлах) | +| `node scripts/check-docs.mjs` (диф трогает `src/**`) | да | «7 files, 12 external links» — зелёный, `sourceFingerprint` в `screenshots.json` уже обновлён коммитом | +| `npm run bundle:budget` | да | initial View 298979 B (потолок 299600±2000); предупреждение о запасе <15000 Б — существующий проектный долг (#367/#474), эта задача добавляет один атрибут разметки и не сдвигает бюджет заметно; не находка этой задачи | +| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | да | «НЕОПРЕДЕЛЁННОСТЬ» — 2 файла, 0 символов на изменённых строках, порог «широкого» не достигнут. Решение ревьюера ниже | +| `node demo/smoke_summary_dialog_scroll.mjs` (AC1/AC2, новый) | да | `OK`, все 14 проверок зелёные (desktop wheel/footer, phone touch/viewport, witness без `flexcontent`) | +| `node demo/smoke_summary_panel_polish.mjs` (AC3, прямое совпадение — единственный смок диалога панели) | да | `OK`; файл смока не менялся (`git diff` пуст) — «без правок его ожиданий» выполнено | +| `node demo/smoke_dialog_modal_recovery.mjs`, `node demo/smoke_esc_dialogs.mjs`, `node demo/smoke_dialog_footer_width.mjs` (зарегистрированная связь: общий `hp-dialog`, фокус/эскейп/футер) | да | все три `OK` | +| `node scripts/mutation-gate.mjs --id=summary-dialog-drops-flex-content` | да | «чистый прогон» → зелёный, мутация → «тест покраснел, как обязан», поймано 1 из 1 | +| `node scripts/mutation-gate.mjs --id=hp-dialog-ignores-flex-content` | да | то же, поймано 1 из 1 | +| in-memory негативная проба AC4 (`test/hp-dialog-contract.test.mjs`, добавление `flex-content` в `hp-confirm.ts` только в памяти интерпретатора, без записи в дерево) | да | ассерт `=== 1` дал бы 2 → тест обязан покраснеть | + +**Не прогонял:** полный `golden:verify`, `python -m pytest tests_backend`, полную матрицу смоков (237 шт.), +performance-профили. + +- `golden` — диф не меняет ни один существующий рендер: атрибут opt-in, `?flexcontent=` в Lit убирает + атрибут целиком при `false`, поэтому разметка всех прочих `hp-dialog` не меняется ни на байт; golden + снимается в demo-стенде, где вообще используется нативная ветка (``), которую диф не трогает. + golden-сцен с префиксом `summary` в репозитории нет (снято ещё в SPEC-REVIEW r1 M1, AC3 переписан на + единственный смок). Риск нулевой — не прогонял намеренно, а не пропустил. +- `pytest tests_backend` — диф не касается `custom_components/houseplan/**/*.py` вовсе. +- Полная матрица смоков и перф-профили — не названы в AC; `smoke-select` вернул «НЕОПРЕДЕЛЁННОСТЬ», выбор + сузил вручную до прямого совпадения (диалог панели) и зарегистрированных связей (общий `hp-dialog`: + восстановление модального стека, Escape-стек, ширина футера). Остальные 231 смока к этому диффу не + привязаны сильнее «общий компонент» и по правилу прогона не требуют. + +## AC → доказательство → чем краснеет + +| AC | Доказано | Чем краснеет | +|---|---|---| +| AC1 (HA-ветка: колесо крутит `.summary-editor`, шапка/футер неподвижны) | `demo/smoke_summary_dialog_scroll.mjs` сценарий (a), прогнан лично — `OK` | оба мутанта `summary-dialog-drops-flex-content` и `hp-dialog-ignores-flex-content`, прогнаны лично: «покраснел, как обязан» | +| AC2 (touch 390×844 крутит редактор; диалог ≤ вьюпорта; футер доступен) | сценарий (b), прогнан лично — `OK` | те же два мутанта (общий guard — атрибут) | +| AC3 (нативная ветка не меняется, `smoke_summary_panel_polish` зелёный без правок ожиданий) | прогнан лично — `OK`; `git diff` файла смока пуст | не защитный AC (сравнение факта с ожиданием, не guard) — правило §2.7 не требует красноты | +| AC4 (прочие `hp-dialog` без `flex-content` не меняют раскладку) | `test/hp-dialog-contract.test.mjs` (в составе `npm test`, прогнан лично) + чтение: `?flexcontent=` в Lit опускает атрибут при `false`, значит разметка девяти прочих вызывающих идентична | in-memory негативная проба (см. выше, без записи в дерево): подмена показала бы `optIns.length === 2` → ассерт `=== 1` красный | +| AC5 (оба мутанта пойманы штатным раннером) | сами прогоны мутантов, см. выше | это и есть доказательство; вторичной пробы не нужно | +| AC6 (`User-Visible: yes`, оба changelog, i18n/модель/перф не задеты) | `git diff` коммита: `docs/CHANGELOG.md` + `docs/CHANGELOG.ru.md` в том же коммите; ни один `translations/**`/`i18n` файл не в диффе; в `src/hp-dialog.ts` нет новых обработчиков — только проброс булева атрибута в существующий рендер | не защитный AC (перечисление фактов), доказано чтением диффа | + +## Что проверено и корректно + +- Обе HA-ветки рендера (`describedBy` и без) получили `?flexcontent=${this.flexContent}` — прочитано в + `src/hp-dialog.ts:515,531`, подтверждено юнит-тестом и мутантом. +- Нативная ``-ветка (`_usesHaDialog()` false) не содержит ни одного упоминания + `flexcontent`/CSS-селектора на `:host([flex-content])` — сброс атрибута не имеет побочных эффектов там, + где используется golden/demo. +- Диагноз (scroll chaining обрывается на height-unbound `overflow:auto + overscroll-behavior:contain`) + independently подтверждён автором на живом стенде HA 2026.9.1 (`ha-dialog.flexContent = true` → + `.summary-editor` 477/819, колесо работает) — это сильнее, чем воспроизведение только в стабе; стаб + смока копирует контракт `.body{overflow:auto}` + `:host([flexcontent]) .body{display:flex}`, о котором + говорит и уже существующий комментарий `demo/capture_summary_panel_505.mjs` (#505). +- `docs/TOUCH-SUPPORT.md:48` прямо называет «simple settings form» сводной панели гарантированной View- + touch-поверхностью — включение touch в скоуп задачи не расширение, а обязательное соответствие контракту. +- Терминология changelog («сводной панели», «настройки») совпадает с `docs/USER-GUIDE.ru.md:264`. +- `docs/images/screenshots.json`: только `sourceFingerprint`/`sourceSha256` изменились, все `imageSha256` + идентичны — соответствует заявленному в хендоффе `docs:accept --identical` (11/11 пиксель-в-пиксель, первое + боевое использование #512). +- Один коммит, трейлеры `Issue: #508` и `User-Visible: yes` на месте; `Release:`/`Baseline-Reviewed:` не + требуются — `demo/golden/baselines/**` не тронут. +- Скоуп не расширен: единственный вызывающий (`summary-panel-editor.ts`) объявляет атрибут, остальные восемь + — нет (проверено и тестом, и построчным чтением). + +## Находки + +Нет. High: 0, Medium: 0, Low: 0. + +## Унаследовано из ревью ТЗ (для полноты трассировки) + +Спек-ревью прошло два раунда (`SPEC-REVIEW-508-r1.md` → жёлтый, `SPEC-REVIEW-508-r2.md` → зелёный на +SHA `f02af9330b17a37eea6c8086f4335cbbc7265d36`): M1 (AC3 ссылался на несуществующий golden-префикс +`summary-*`) и M2 (не было раздела «откат») закрыты правкой тела issue. Код-ревью это не переоткрывает — +AC3 в теле issue на момент код-ревью уже опирается только на `smoke_summary_panel_polish`, а раздел +«Откат» присутствует и соответствует факту (opt-in атрибут, ревёрт коммита без миграций). + +## Вердикт + +Зелёный. Все AC доказаны исполнением (кроме AC3/AC6, не заявляющих защиты — доказаны прямым сравнением), +диагноз независимо подтверждён на реальном стенде HA, оба защитных мутанта лично прогнаны и красные без +патча/зелёные с ним, регресс на нативную ветку и остальные восемь `hp-dialog` исключён и тестом, и +построчным чтением кода. Находок нет. + +--- + + + +## Материал раунда + +- Ветка: `issue/508-summary-dialog-scroll`, коммит `7e5d394a936a` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. +- Дерево материала: `1594677359b610e22fc5886db02f66bc3c943322` + ``` + git log --all --format='%H %T' | grep 1594677359b6 + ``` +- Вердикт конвейера: `green` · High 0