Files
houseplan-card/docs/reviews/CODE-REVIEW-597-r1.md
T
2026-09-19 14:19:54 +00:00

203 lines
20 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-597-r1
Issue: #597 · Этап: код-ревью · Заход: r1 · блокирующих циклов израсходовано 0/2 (лёгкий трек, лимит 2)
Материал: `2b89f699618a3fa3e9bafc07dfa71e07a8e19960` (рабочая копия), диапазон `git diff origin/dev...HEAD`
Ветка приведена конвейером к актуальному `dev` до ревью (`45dac1c0` → `2b89f699`, +2 коммита `dev` сверху) —
разбор проведён полностью (§7.2), не по дельте.
## Скоуп
Шаг 2 эпика #591 (шаг 1 — #594, в `dev`). Генератор общего набора контролов
(`src/styles/form-kit.styles.ts`) разрезан на пять именованных фрагментов
(`formKitCardsCss`, `formKitFocusCss`, `formKitDisabledCss`, `formKitSwitchRowCss`,
`formKitSwitchCaptionCss`); редактор боковой панели (`src/summary-panel-editor-style.ts`)
подставляет каждый на своё место вместо собственной копии тех же правил.
Ни одна строка `src/summary-panel-editor.ts` (разметка) не меняется. Изменение
задумано как «ноль видимых изменений» — К1–К5 ТЗ.
Диапазон диффа (без сгенерированного класса D): `src/styles/form-kit.styles.ts`,
`src/summary-panel-editor-style.ts`, `scripts/mutation-registry.mjs`,
`test/form-kit.test.mjs`, три новые фикстуры в `test/fixtures/`. Класс A задет —
полный флоу обязателен и пройден (ТЗ в теле issue, ревью ТЗ зелёное на r2).
## Как проверялось
Дешёвые гейты (`typecheck`, `npm test`, `npm run build` со сверкой бандла) на этом
SHA уже подтверждены зелёным Validate
(https://github.com/Matysh/houseplan-card/actions/runs/35446978510) — не
перегонялись. Вместо этого бюджет раунда потрачен на чтение кода и на гейты,
которые Validate в этом прогоне **не покрывает**: в нём `smoke`, `golden` и
`performance_smoke` стоят `skipped` (обычный push в задачную ветку, не кандидат
беты и не PR — эти джобы включаются только на `heavy`).
| Гейт | Прогнан | Результат |
|---|---|---|
| `npx tsc --noEmit`, `npm test`, `npm run build` + сверка 3 копий бандла | нет — подтверждено зелёным Validate на этом SHA | success (ссылка выше) |
| `npm run bundle:sync` (пересборка + сверка дерева) | да, лично | `dist/houseplan-card.js` побайтово совпал с закоммиченным после чистой пересборки — `git status` пуст |
| `npm run bundle:budget` (AC7) | да, лично | initial View 290843/291400, lazy editor 222359/222900, lazy furniture art 16942/17900 — совпало цифра в цифру с хендоффом |
| `node scripts/no-new-any.mjs --base origin/dev --head HEAD` | да | «Новых any нет» |
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | да | одна зарегистрированная связь — `smoke_summary_panel_polish.mjs` ← `summaryPanelEditorCss`; остальных совпадений нет (диф — 2 файла, 9 символов на изменённых строках) |
| `node demo/smoke_summary_panel.mjs` (AC4) | да, лично | `OK` |
| `node demo/smoke_summary_panel_polish.mjs` (AC4) | да, лично | `OK` |
| `node demo/smoke_room_settings.mjs` (AC5) | да, лично | `OK` |
| `npm run golden:verify` (AC6) | да, лично (в песочнице есть Chromium — у автора не было) | 80/80 сцен `passed`, exit 0 — расхождений ноль |
| `node scripts/check-docs.mjs` (обязателен — diff трогает `src/**`) | да, лично | **ERROR: отпечаток скриншотов документации устарел** — см. находку M1 |
| `npx tsc -p tsconfig.test.json && node scripts/fix-test-build.mjs && node --test test/form-kit.test.mjs` | да, лично | 5/5 тестов зелёные |
| Мутант `form-kit-fragment-drifts-from-panel` (padding 14px→13px) | да, лично, дважды (с восстановлением) | оба теста `#597` краснеют: `AssertionError` на побайтовом сравнении |
| Проба M1 ревью ТЗ (литерал фрагмента карточки-группы в блочном комментарии рядом с листом панели) | да, лично | тест 1 краснеет ровно заявленным текстом: `карточка-группа: правило осталось литералом...`; после снятия — снова зелёный |
| Проба на стык фрагментов диалогов (лишний `\n` в `sharedCss`) | да, лично | тест 2 (`#597 лист диалогов карточки не изменился ни на байт`) краснеет |
| Независимая пересборка фикстур с кода `origin/dev` (не доверяя хендоффу) | да, лично | панель: 14585 байт, побайтово совпало с `test/fixtures/summary-panel-editor.css`; диалоги без переключателя — 1885 байт, с ним — 2399 байт, оба побайтово совпали со своими фикстурами |
| `python -m pytest tests_backend -q` | нет | диф не касается `custom_components/**/*.py` |
| Perf-профили | нет | не названы в AC, диф не трогает чувствительные пути (`src/iso-*`, `src/live-*`, `src/render-*`) |
| `npm run docs:accept -- --identical` (диагностика находки M1) | да, лично (изменения отменены, не коммитились) | «Все 11 кадров попиксельно совпали с закоммиченными: принят только отпечаток исходников» |
Не гонял полный набор смоков (250 в матрице) — задача узкая (2 файла CSS, один
потребитель), `smoke-select` называет одну связь, остальные не относятся к
диффу; прогон всей матрицы — предрелизная обязанность, не гейт ревью.
## Находки
### M1 (Medium, в скоупе задачи) — устаревший обязательный гейт `check-docs` не закрыт
`node scripts/check-docs.mjs` на материале ревью красит:
```
ERROR screenshot source fingerprint is stale; run npm run docs:capture and accept before the beta candidate (#479)
```
PROCESS.md §8 называет этот гейт безусловно обязательным при любом диффе по
`src/**`: «отпечаток скриншотов документации считается по всему `src/**`,
поэтому любая правка фронтенда делает его устаревшим... выборка «по diff и AC»
здесь не работает — diff всегда попадает, и решать нечего». Диф задачи меняет
`src/styles/form-kit.styles.ts` и `src/summary-panel-editor-style.ts` — значит
гейт применим и обязан быть закрыт в рамках задачи, а не оставлен следующей.
Это не гипотетический риск: ровно тот же паттерн уже стоил продукту инцидента —
«скриншоты не пересняли в #230 и #234, и `dev` стоял с красным job `docs`, пока
это не нашли при следующей задаче (#237)» (PROCESS.md §8). Непосредственный
предшественник этой ветки — коммит `fc4d55c7 docs: отпечаток скриншотов после
правки комментария рантайма (#593)` — обновил отпечаток сразу после последней
правки `src/**` перед этой задачей (`0565c7ad`, #593); других правок `src/**`
между `fc4d55c7` и материалом ревью нет, кроме самой задачи #597. То есть
именно этот коммит впервые за долгое время сделал отпечаток снова устаревшим
и не закрыл это следующим шагом, хотя предыдущая задача сделала это ровно так.
Проверил, что правка дешёвая и не открывает новый спор с ТЗ: прогнал
`npm run docs:accept -- --identical` — «Все 11 кадров попиксельно совпали с
закоммиченными: принят только отпечаток исходников» (0 расхождений, как и
обещает К1/AC1 задачи — вывод CSS не изменился ни на пиксель). Изменения не
коммитил (ревьюер не правит материал); откатил рабочую копию.
Раздел ТЗ «Release-артефакты» («Документация не меняется») отвечает только за
прозу (`docs/*.md`, `USER-GUIDE.ru.md`) и не называет отпечаток скриншотов
отдельно — в этом смысле находка и к ТЗ (пропущенный release-артефакт), и к
реализации (гейт не прогнан и не закрыт до хендоффа). Ни AC1–AC8, ни хендофф
не упоминают `check-docs`, хотя PROCESS.md называет его обязательным именно по
факту диффа `src/**`, независимо от AC.
**Чем краснеет:** сам гейт — `node scripts/check-docs.mjs` без флагов (умолчание
`--screenshots=strict`) на материале ревью. В обычном пуше в задачную ветку CI
джоб «Предполёт» использует `--screenshots=warn` и поэтому зелёный на этом SHA —
это маскирует находку в Validate, но не отменяет требование PROCESS.md §8 и не
меняет исход на кандидате беты, где режим строгий.
**Правка:** `npm run docs:accept -- --identical` и коммит обновлённого
`docs/images/screenshots.json` (только отпечаток, без изменения PNG) в этой же
задаче — ровно то, что сделал непосредственный предшественник (`fc4d55c7`).
Находка в скоупе задачи (диф сам её и вызвал) — по §2.7/§7.2 чинится в текущем
issue, отдельный issue не заводится.
## Что проверено и корректно
- **К1/AC1 (ноль видимых изменений в листе панели).** Независимо от хендоффа
пересобрал `formKitCss`-composition на коде `origin/dev` (через временный
откат `src/styles/form-kit.styles.ts` к версии `dev` и компиляцию) и сравнил
результат с закоммиченной фикстурой: 14585 байт, побайтовое совпадение.
Тест `test/form-kit.test.mjs` подтверждает это же на актуальном коде.
- **К2/AC2 (одна копия).** Прочитал `src/summary-panel-editor-style.ts` — ни
один из пяти литералов `PANEL_FRAGMENTS` не встречается в файле, только вызовы
`formKit*Css(SUMMARY_PANEL_FORM_KIT)`. Мутационная проба (литерал
карточки-группы в блочном комментарии рядом с листом) краснит тест ровно
словами хендоффа; без правки M1 прежняя проверка (`min-height: 54px`) её бы
пропустила — это подтверждено чтением истории обеих находок ревью ТЗ r1/r2.
- **К3/AC3 (генератор не меняет смысла листа диалогов).** Пересобрал
`formKitCss(CARD_DIALOG_FORM_KIT)` в обеих формах на коде `origin/dev` и
сравнил с новыми фикстурами: без переключателя 1885 байт, с ним 2399 —
побайтовое совпадение в обоих случаях, независимо от заявленного в хендоффе.
Проба с лишним переносом строки на стыке фрагментов (`sharedCss`) краснит
именно этот тест — свидетель реален, а не декларативен.
- **К4 (граф загрузки).** Ни `formKitCardsCss`/`formKitFocusCss`/... не
экспортированы вовне лениво загружаемых модулей за пределы прежних; `git diff
--stat` подтверждает изменения только в тех же двух файлах, что и раньше несли
этот CSS. `npm run bundle:budget` — initial View / lazy editor / lazy furniture
art в пределах прежних потолков, цифры совпали с хендоффом.
- **К5 (разметка панели не тронута).** `src/summary-panel-editor.ts` вне диффа
(`git diff --name-only -- src/`).
- **AC4/AC5 (смоки панели и диалога комнаты).** Прогнал все три смока лично:
`smoke_summary_panel.mjs`, `smoke_summary_panel_polish.mjs`,
`smoke_room_settings.mjs` — все `OK`.
- **AC6 (эталоны golden).** Автор не прогонял (в песочнице нет Chromium) — в
этой среде Chromium доступен, прогнал сам: 80/80 сцен `passed`, exit 0.
Расхождений нет ни в одной сцене, включая те, что рендерят диалоги на общем
наборе контролов (диалог комнаты и т.п.).
- **AC7 (бюджет бандла).** Числа совпали цифра в цифру с хендоффом.
- **AC8 (полный набор).** `npm test` и `npm run gate:small` подтверждены зелёным
Validate на этом SHA; отдельно прогнал целевой файл `test/form-kit.test.mjs`
(5/5) и `no-new-any` (чисто).
- **Мутант `form-kit-fragment-drifts-from-panel`.** Зарегистрирован в
`scripts/mutation-registry.mjs`, гард — сам целевой тест. Применил мутацию
(`padding: 14px` → `13px`) дважды независимо — оба раза оба теста `#597`
падают с `AssertionError`; без мутации — зелёные. Мутант реален, не
декоративен.
- **Провенанс коммита.** Один коммит, трейлеры `Issue: #597` и
`User-Visible: no` на месте; `User-Visible: no` не требует changelog, диф не
трогает пользовательский текст ни в одном из двух changelog-файлов.
- **i18n.** Диф не касается i18n-файлов — согласуется с ТЗ («i18n: нет»).
- **Классы риска §2.6.** async/данные и права/геометрия — неприменимо (чистый
CSS-рефакторинг без сети, без геометрии). Визуал — главный риск, закрыт
двойным свидетелем: побайтовым текстовым сравнением (сильнее кадра, ловит
перестановку правил, которую пиксель не отличит) и личным прогоном
`golden:verify` на полной матрице. Объём/perf — AC7 подтверждён. Host/input —
разметка и обработчики не менялись (К5), поведение то же.
- **Факт из ТЗ, использованный как опора доказательства («у панели нет
golden-кадра»).** Проверил `demo/golden/matrix.mjs` — сцены с `dialog:
'summary'` действительно нет. Утверждение не является догадкой.
## Чего не проверял
- Полный набор из 250 браузерных смоков — не оправдано диффом (`smoke-select`
называет одну прямую связь, диф — 2 CSS-файла с нулевым изменением
собранного вывода для панели и диалогов карточки).
- `python -m pytest tests_backend` — диф не касается Python.
- Performance-профили — не названы в AC, диф не в чувствительных путях
(`src/iso-*`, `src/live-*`, `src/render-*`, `houseplan-card.ts`).
- Полный HA-харнесс (`fcntl`, недоступен вне Linux+HA) — не относится к CSS.
- `process-gate.mjs --issues` — не имеет значения для решения ревьюера, статус
и лейблы issue уже видны напрямую через `gh`.
- `npm run docs:accept -- --identical` — прогнал только для диагностики находки
M1 (подтвердить, что правка дешёвая), результат не коммитил: правку делает
автор в задаче.
## Вердикт
Жёлтый. Единственная находка (M1, Medium) — в скоупе задачи, вызвана самим
диффом и чинится одной командой плюс коммитом отпечатка; отдельный issue не
заводится. Все восемь AC доказаны свидетелями, которые я лично проверил на
умение краснеть (мутант + две пробы совпали с заявленными в хендоффе текстами
ошибок); AC6 закрыт мной вместо автора, у которого не было среды. Технический
план задачи (разрезание генератора на фрагменты, подстановка вместо
редизайна) корректен и полностью соответствует контракту К1–К5.
---
<!-- material-anchors: сгенерировано конвейером (#414) -->
## Материал раунда
- Ветка: `issue/597-panel-on-form-kit`, коммит `2b89f699618a` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
- Дерево материала: `94639a89f4617d5498d0a2c63027db0fa2dab139`
```
git log --all --format='%H %T' | grep 94639a89f461
```
- Тело issue: `8a56815128285d56f2c2f9f63eb6d8ae211525202f38e8bdd75159aa7c55412c`
- Вердикт конвейера: `yellow` · High 0