Files
houseplan-card/docs/reviews/CODE-REVIEW-508-r2.md
T
2026-09-09 20:02:08 +00:00

194 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CODE-REVIEW-508-r2
Issue: #508 — «Сводная панель: в диалоге настроек не работает прокрутка — колесом мыши и touch на мобильных»
Материал: `6ac513caa09a8e583869879412aa3a0c67d04488` (рабочая копия уже на нём). Ветка приведена конвейером к
`dev` до ревью: `e23d742c → 6ac513ca`, поверх легло 1 коммит `dev` (несвязанный — `docs: review document
for #502`).
Заход: r2 · блокирующих циклов израсходовано 0 из 4. Ребейз на ушедший вперёд `dev` формально делает
дерево другим (§7.2) → разбор полный, а не по дельте.
## Почему вообще r2, если r1 был зелёным без находок
Код-ревью r1 (`docs/reviews/CODE-REVIEW-508-r1.md`, материал `768e1281`) уже завершилось вердиктом
«зелёный, находок нет» с исчерпывающим личным прогоном гейтов, мутантов, смоков и подтверждением автора
на живом стенде HA. Сразу после публикации вердикта автор сообщил в issue: «Автоматическое ревью не
отработало» — статусная метка не переставилась, конвейер оставил задачу на месте. Затем конвейер
перебазировал ветку на успевший уйти вперёд `dev` (там landing получил несвязанный коммит с ревью-доком
для issue #502) и передал её на повторное ревью уже под новым SHA. Автор с момента r1 в код и ТЗ не
вносил ничего — это не возврат на правки, а техническое повторение ревью из-за сбоя механики метки плюс
ребейз. Поэтому раздела «находка → возврат → правка» в этом раунде по существу нет: проверка ниже —
подтверждение, что ребейз не изменил и не сломал ничего, а не разбор чьих-то правок.
## Скоуп
Не изменился с r1: один функциональный коммит (`e38beed7`, экс-`768e1281`) — класс A (`src/hp-dialog.ts`,
`src/summary-panel-editor.ts`) + класс B (новый смок `demo/smoke_summary_dialog_scroll.mjs`, контрактный
юнит-тест `test/hp-dialog-contract.test.mjs`, два мутанта в `scripts/mutation-gate.mjs`) + класс C
(`docs/ARCHITECTURE.md`, оба CHANGELOG) + класс D (пересобранный бандл во всех копиях,
`docs/images/screenshots.json` — только фингерпринт). `hp-dialog` получает opt-in булев атрибут
`flex-content` (`?flexcontent` на `ha-dialog`, обе HA-ветки рендера); нативная `<dialog>`-ветка не тронута.
Диалог настроек сводной панели его объявляет — единственный из десяти вызывающих `<hp-dialog>`.
J1 (`docs/SCOPE.md`) — обзорная сводная панель входит в «show the whole home at a glance»; чистый
баг-фикс регресса после #505, скоуп не расширен, продукт не меняет договор.
## Как проверялось (это не дельта — это независимая перепроверка «ничего не изменилось»)
Первый шаг — не поверить заявлению «ребейз ничего не тронул», а доказать его:
```
git diff 768e1281 e38beed7 --stat # r1-материал vs текущий фикс-коммит
```
Результат: единственная разница — файл `docs/reviews/SPEC-REVIEW-502-r1.md` (несвязанный коммит `dev` для
issue #502). Все файлы продукта — `src/hp-dialog.ts`, `src/summary-panel-editor.ts`,
`test/hp-dialog-contract.test.mjs`, `docs/ARCHITECTURE.md`, оба CHANGELOG, `scripts/mutation-gate.mjs`,
`demo/smoke_summary_dialog_scroll.mjs`, весь пересобранный бандл — побайтово идентичны материалу r1.
`docs/images/screenshots.json` проверен построчно (`grep imageSha256` на диффе): изменились только
`sourceFingerprint`/`sourceSha256`-контекст, ни один `imageSha256` не сдвинулся.
Это и определило объём: код доказанно тот же, что уже получил зелёный вердикт с личными прогонами гейтов,
мутантов и смоков (не только заявлением автора — заявление в r1 тоже было перепроверено исполнением). Все
выводы AC1–AC6 из r1 остаются в силе автоматически, потому что применяются к тому же байт-в-байт коду. Тем
не менее раунд объявлен «полным» (ребейз, §7.2), поэтому ниже — не просто ссылка на r1, а собственный
прогон части гейтов на текущем SHA `6ac513ca`, чтобы исключить эффекты самого ребейза (окружение сборки,
конфликт слияния бандла и т. п.), а не только идентичность текста диффа.
### Дешёвые гейты
Validate на `6ac513ca` зелёный (дано в задаче ревью, https://github.com/Matysh/houseplan-card/actions/runs/34397342070)
— `tsc`, `npm test` (2436/2436), `npm run build` со сверкой бандла уже подтверждены конвейером на этом
самом SHA, повторно не гонял.
### Прогнано лично на `6ac513ca` (не на материале r1 — на текущем SHA)
| Гейт/смок | Результат |
|---|---|
| `node --test test/hp-dialog-contract.test.mjs` (AC1 текстовая часть, AC4) | 3/3 pass |
| `node scripts/check-docs.mjs` (диф трогает `src/**`) | «Documentation checks passed (7 files, 12 external links)» — совпадает с числом из r1 |
| `node scripts/bundle-sync.mjs` (пересборка + раскладка по трём местам: `dist`, `custom_components/houseplan/frontend`, `demo/srv/assets`) | выполнено; `git status --porcelain` после — пусто (отслеживаемые копии не разошлись с коммитом) |
| `node demo/smoke_summary_dialog_scroll.mjs` (AC1/AC2, прямое совпадение) | `OK` |
| `node demo/smoke_summary_panel_polish.mjs` (AC3, прямое совпадение — файл смока не менялся) | `OK` |
| `node demo/smoke_dialog_modal_recovery.mjs` (зарегистрированная связь: общий `hp-dialog`) | `OK` |
| `node demo/smoke_esc_dialogs.mjs` (зарегистрированная связь) | `OK` |
| `node demo/smoke_dialog_footer_width.mjs` (зарегистрированная связь) | `OK` |
| `node scripts/mutation-gate.mjs --id=summary-dialog-drops-flex-content` | чистый прогон OK → мутация: «тест покраснел, как обязан» → поймано 1 из 1 |
| `node scripts/mutation-gate.mjs --id=hp-dialog-ignores-flex-content` | то же → поймано 1 из 1 |
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | «НЕОПРЕДЕЛЁННОСТЬ» — 2 файла `src/**`, 0 символов на изменённых строках (тот же результат, что в r1); выбор смоков выше сделан вручную: прямое совпадение (диалог панели) + зарегистрированные связи (общий `hp-dialog`) |
| `grep -rn "<hp-dialog" src/` | 10 вызывающих (включая `summary-panel-editor.ts`); ровно список из `test/hp-dialog-contract.test.mjs` AC4, opt-in — один |
Изначально попытка запустить браузерные смоки провалилась ошибкой окружения
(`Failed to fetch dynamically imported module: http://demo.local/...`, `page.waitForFunction` timeout) —
это была не регрессия, а отсутствие локальной копии `demo/srv/assets` (она не хранится в репозитории,
`scripts/bundle-sync.mjs`: «копий две с половиной», рабочая копия стенда собирается заново). Тот же
таймаут воспроизводился на `demo/smoke_summary_panel_polish.mjs`, файл которого этот диф не трогает вовсе
— это подтвердило, что причина в окружении ревью-песочницы, а не в коде. После `node
scripts/bundle-sync.mjs` (пересборка из уже собранного `dist/`) оба смока пошли зелёными.
## Закрытие раунда r1
Код-ревью r1 не имело находок (High 0, Medium 0, Low 0) — блокирующих находок для закрытия нет. Раунд r2
открылся не как возврат на правки, а как техническое повторение из-за несработавшей автоматической
перестановки метки после зелёного вердикта r1, дополненное последующим ребейзом на ушедший вперёд `dev`
(конвейер, не автор). Автор кода/ТЗ с момента r1 не менял — независимо доказано выше диффом
`768e1281..e38beed7`.
| Находка r1 | Статус | Где видно |
|---|---|---|
| (нет находок) | — | `docs/reviews/CODE-REVIEW-508-r1.md`, раздел «Находки»: «Нет. High: 0, Medium: 0, Low: 0» |
## Унаследовано из r1
Без повторного самостоятельного вывода, но с проверкой байт-в-байт идентичности кода (см. «Как
проверялось»), наследую из `docs/reviews/CODE-REVIEW-508-r1.md` (материал `768e1281f9119b12d018b416372984ff17eae62d`,
дерево `1594677359b610e22fc5886db02f66bc3c943322`):
- диагноз (scroll chaining обрывается на height-unbound `.summary-editor` под `ha-dialog`, `overflow:
auto` + `overscroll-behavior: contain`) — независимо подтверждён автором на живом стенде HA 2026.9.1, не
только в стабе смока;
- личные прогоны `tsc`, `npm test` (2437/2436 pass+1 skip), `no-new-any`, `bundle:budget` на материале
r1 — не переигрывал повторно, т. к. Validate CI зелёный на `6ac513ca` покрывает те же проверки на
байт-в-байт идентичном коде;
- вывод AC6 (i18n/модель/перф не задеты, трейлеры `Issue:`/`User-Visible: yes` и оба CHANGELOG в одном
коммите) — перепроверен построчно, см. «AC → доказательство» ниже, поэтому это скорее «переподтверждено»,
чем слепо унаследовано;
- трассировку к спек-ревью (`SPEC-REVIEW-508-r1.md` жёлтый → `SPEC-REVIEW-508-r2.md` зелёный на SHA
`f02af9330b17a37eea6c8086f4335cbbc7265d36`, M1/M2 закрыты правкой тела issue) — код-ревью r1 её не
переоткрывало, r2 тоже не переоткрывает: текст issue с тех пор не менялся.
## AC → доказательство → чем краснеет (переподтверждено лично на `6ac513ca`)
| AC | Доказано | Чем краснеет |
|---|---|---|
| AC1 (HA-ветка: колесо крутит `.summary-editor`, шапка/футер неподвижны) | `demo/smoke_summary_dialog_scroll.mjs` сценарий (a) — `OK`, лично | оба мутанта, лично: «покраснел, как обязан» |
| AC2 (touch 390×844 крутит редактор; диалог ≤ вьюпорта; футер доступен) | сценарий (b) — `OK`, лично | те же два мутанта |
| AC3 (нативная ветка не меняется) | `smoke_summary_panel_polish` — `OK`, лично; файл смока не в диффе | не защитный AC |
| AC4 (прочие 9 `hp-dialog` без `flex-content`) | `test/hp-dialog-contract.test.mjs` — 3/3 pass, лично; `grep` по `src/**` — 10 вызывающих, 1 opt-in | ассерт `=== 1` в тесте (in-memory проба r1 сохраняет силу — код теста не менялся) |
| AC5 (оба мутанта пойманы) | оба мутанта лично прогнаны — «поймано 1 из 1» каждый | это и есть доказательство |
| AC6 (`User-Visible: yes`, оба changelog, i18n/модель/перф не задеты) | `git show --stat e38beed7`: `docs/CHANGELOG.md`+`docs/CHANGELOG.ru.md` в том же коммите; `git log -1` коммита содержит `Issue: #508` и `User-Visible: yes`; ни один `i18n`/`translations` файл не в диффе; `src/hp-dialog.ts` diff — только проброс булева атрибута, новых обработчиков нет | не защитный AC, доказано чтением |
## Одно число — один источник
Диф не вводит и не дублирует ни одну видимую пользователю величину (площадь, значение, подпись): меняется
только внутренняя раскладка скролл-контейнера через булев атрибут. `test/single-source-numbers.test.mjs`
не затрагивается и не требуется для этой правки.
## Что проверено и корректно
- Обе HA-ветки рендера (`describedBy` и без) несут `?flexcontent=${this.flexContent}` — строки
`src/hp-dialog.ts:515,531`, подтверждено и тестом, и мутантами лично.
- Нативная `<dialog>`-ветка не содержит `flexcontent`/связанных селекторов — регресс на неё исключён и
тестом (`smoke_summary_panel_polish`), и построчным чтением.
- `docs/TOUCH-SUPPORT.md:48–49` называет «simple settings form» сводной панели гарантированной View-touch-
поверхностью — включение touch в скоуп не расширение, а обязательное соответствие контракту.
- Терминология CHANGELOG.ru.md («сводной панели», «настроек») совпадает с `docs/USER-GUIDE.ru.md:264`
(«Настройки сводной панели»).
- `docs/images/screenshots.json`: только `sourceFingerprint`/`sourceSha256`; все 11 `imageSha256`
идентичны — докс-скриншоты не изменились по существу.
- Один коммит (`e38beed7`), трейлеры `Issue: #508` и `User-Visible: yes` на месте.
- После `bundle-sync` рабочая копия чиста (`git status --porcelain` пуст) — три копии бандла (`dist`,
`custom_components/houseplan/frontend`, `demo/srv/assets`) согласованы с закоммиченным.
- Ребейз сам по себе не внёс никакого продуктового изменения — доказано диффом, а не заявлено.
## Находки
Нет. High: 0, Medium: 0, Low: 0.
## Чего не проверял и почему
- `npm run golden:verify` — атрибут opt-in, `?flexcontent=` в Lit убирает атрибут целиком при `false`,
разметка прочих `hp-dialog` не меняется ни на байт; golden-сцен диалога сводной панели в репозитории нет
(снято ещё в `SPEC-REVIEW-508-r1.md` M1). Риск нулевой, решение то же, что в r1, переподтверждено тем же
рассуждением на идентичном коде.
- `python -m pytest tests_backend` — диф не касается `custom_components/houseplan/**/*.py`.
- Полная матрица смоков (237 шт.) и перф-профили — не названы в AC; `smoke-select` вернул
«НЕОПРЕДЕЛЁННОСТЬ» (2 файла `src/**`, 0 символов на изменённых строках, порог «широкого» — 47 смоков).
Выбор сужен вручную до прямого совпадения (`smoke_summary_dialog_scroll`, `smoke_summary_panel_polish`) и
зарегистрированных связей на общий `hp-dialog` (`smoke_dialog_modal_recovery`, `smoke_esc_dialogs`,
`smoke_dialog_footer_width`) — все пять прогнаны лично, все зелёные.
- `npm run invariants` — диф не трогает геометрию/рёбра комнат/`layout`/`marker.space`/`open_spans`, гейт не
применим.
- Полный `npm test` (все 2436 юнитов) — не переигрывал повторно вживую: Validate CI уже подтвердил его
зелёным на этом самом SHA `6ac513ca` (ссылка выше), а не на другом; вместо повторного полного прогона
лично выполнен целевой `test/hp-dialog-contract.test.mjs`.
## Вердикт
Зелёный. Разбор — полный (ребейз, §7.2), но не «с нуля»: доказано диффом, что код байт-в-байт идентичен
уже одобренному материалу r1, и это доказательство переподтверждено собственными прогонами гейтов, смоков
и мутантов на текущем SHA `6ac513ca`, а не принятием чужого вердикта на веру. Находок нет. Раунд не
образует цикл (§4) — вердикт зелёный.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/508-summary-dialog-scroll`, коммит `e23d742cd508` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `5fc02b5d1de102ed9770f28fc5eb3a41098cbc11`
```
git log --all --format='%H %T' | grep 5fc02b5d1de1
```
- Вердикт конвейера: `green` · High 0