Files
houseplan-card/docs/reviews/CODE-REVIEW-582-r3.md
T
claude[bot] 5867ee02b6
Проверка (CI) / Классификация изменённых файлов (push) Successful in 33s
Проверка (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) / HACS: валидация репозитория (push) Failing after 46s
Проверка (CI) / Hassfest: манифест интеграции (push) Failing after 59s
Проверка (CI) / Предполёт: документация, провенанс, процесс (push) Failing after 1m34s
Проверка (CI) / Переиспользование: это дерево уже проверено (push) Successful in 1m22s
Проверка (CI) / Геометрия: TS/Python parity исполнена (push) Successful in 3m28s
Проверка (CI) / Фронтенд: типы, юниты, мутанты, синхрон бандла (push) Failing after 9m8s
Проверка (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) / Бэкенд: pytest в Home Assistant (push) Failing after 6m3s
Проверка (CI) / Доказательство выполненных проверок (push) Failing after 16s
docs: review document for #582
Issue: #582
User-Visible: no
2026-09-15 11:10:06 +00:00

17 KiB
Raw Blame History

CODE-REVIEW-582-r3

Issue: #582 — HA Companion: на плане с масштабом 1 см/точку при pinch появляются фиксированные белые области.

Материал: 54dc836deec3318e2eac2349f17945b0e6738adb (рабочая копия была на нём). Заход r3 · блокирующих циклов ревью 2 из 4 израсходовано.

Скоуп разбора

Это третий заход, и красный вердикт r2 был получен предметно на материале 226f3230073afd79a1eef9e2e5979684c9ae628f7 (docs: review document for #582 / a0a108cf — публикация документа r2 — легла поверх него как чистый commit класса C, дельту кода не меняла). Автор не ребейзился на ушедший вперёд dev и не менял контракт поведения — правка адресует ровно две находки r2 в той же архитектуре (hp-safe-daycycle-outline), поэтому разбор ведётся по дельте:

git diff 226f3230..54dc836d

54 файла, из них продуктовый код (класс A) — src/houseplan-card.ts, src/space-card.ts, src/space-render.ts, src/styles/plan.styles.ts; гейты/тесты (класс B) — demo/smoke_bg_color.mjs, demo/smoke_daycycle_layer_budget.mjs, demo/smoke_smooth_zoom.mjs, scripts/mutation-registry.mjs, scripts/smoke-links.mjs, test/paper-scene-contract.test.mjs; документация (класс C) — docs/ARCHITECTURE.md, docs/SUN.md, docs/TESTING.md, docs/images/screenshots.json (только fingerprint); остальное — сгенерированный бандл (класс D, dist/**, custom_components/houseplan/frontend/**). Геометрия комнат/рёбер/толщины стен не затронута — npm run invariants не требуется.

Закрытие раунда r2

Находка r2 Чем закрыта Где это видно
High: контракт п.6 не выполнен — .hp-static-stage.daycycle .hp-paperg не тронута дельтой r1→r2, статическая houseplan-space-card сохраняла координатно-большой will-change: filter, симметрии с полной карточкой не было. renderSpaceStatic теперь с первого кадра рендерит отдельный <svg class="hp-paper-outline-svg" ... pointer-events="none"> через общий renderPaperShapes (тот же helper, что и у hp-static-plan-svg); .hp-static-stage.daycycle .hp-paperg получила явные filter: none; will-change: auto; transition: none, а фильтр/will-change: filter переехал на .hp-static-stage.daycycle .hp-paper-outline-svg. src/space-render.ts:902-906, src/styles/plan.styles.ts:93-95,103-108, src/space-card.ts:960-963. Лично прогнан node demo/smoke_daycycle_layer_budget.mjs: staticCardUsesSeparateFilteredOutline, staticCardPaperStaysUnfiltered, staticCardHasOneOutlineLayer, staticCardOutlineLayerIsStageBounded, staticCardHasNo4096ContentLayer — все true, staticOversized: []. Мутант daycycle-static-outline-promoted-on-inner-paper (возвращает will-change: filter на .hp-static-stage.daycycle .hp-paperg) лично прогнан через node scripts/mutation-gate.mjs --id=daycycle-static-outline-promoted-on-inner-paper — заявленный guard (smoke_daycycle_layer_budget.mjs) на чистом коде зелёный, на мутанте красный (1/1 поймано).
Medium (в скоупе): программные переходы камеры без прямого pointer-жеста — double-tap-to-fit, «Вписать всё», клик по комнате, колесо/кнопки zoom — идут через _startCameraTransition, которая нигде не вызывала _activateSafeDayCycleOutline(); планшет-киоск на одних кнопках/double-tap весь сеанс оставался в исходной топологии риска. _startCameraTransition() теперь вызывает this._activateSafeDayCycleOutline() первой строкой, до this._cameraTransition.start(...) — единственная точка входа для room, wheel, button, fit/home/double-tap (все они вызывают именно _startCameraTransition, прямой pinch/pan по-прежнему активирует тот же флаг из своих исходных точек на pointer-событиях). src/houseplan-card.ts:1273-1287 (сам вызов), вызовы _startCameraTransition на houseplan-card.ts:6381 (room), 6739 (wheel), 6756 (button), 6769 (resetZoom: fit/home/double-tap). Мутант daycycle-programmatic-camera-skips-safe-outline (удаляет строку вызова) лично прогнан через node scripts/mutation-gate.mjs --id=daycycle-programmatic-camera-skips-safe-outline — guard smoke_smooth_zoom.mjs зелёный на чистом коде, красный на мутанте (1/1 поймано). Лично прогнан сам смок: programmaticCameraActivatesSafeDayCycleOutline: true (кейс — нажатие кнопки zoom, _stepZoom, идущее через _startCameraTransition).

Оба High/Medium из r2 были в скоупе задачи и почищены в той же ветке без нового issue — ровно так, как требует правило §12/решение 2026-08-19.

Унаследовано из r2 (и раньше)

Без повторной проверки принято то, что дельта 226f3230..54dc836d не задевает и что уже было предметно закрыто в предыдущих раундах:

  • AC1–AC3 (экранно-ограниченный слой контура основной карточки, мутант на внутреннюю .hp-paperg) — закрыты в r1, документ docs/reviews/CODE-REVIEW-582-r1.md, подтверждены на SHA красного r1 (8e225582→ дальше не переоткрывались); архитектура hp-safe-daycycle-outline как отдельный <svg>-сосед, устраняющая implicit-overlap слой plan-svg, — закрыта в r2, документ docs/reviews/CODE-REVIEW-582-r2.md на SHA 226f3230. Дельта r2→r3 не трогает .stage.daycycle (основную интерактивную карточку) в части этого механизма, только добавляет вызов активации в новой точке входа.
  • Item 5 контракта (один внешний контур без внутренних швов, L-формы не превращаются в прямоугольник) — логика объединения paper-силуэтов (renderPaperShapes, вычисление paperShapes) не изменена дельтой r2→r3.
  • AC9 (полевая приёмка на реальном HA Companion) — вне автоматических ворот по определению ТЗ, ответственность владельца в следующей бете.

Переподтверждено самостоятельно, а не просто унаследовано, хотя дельта их прямо не трогает — потому что относятся к тому же визуальному контракту, который теперь исполняется на объединённом (главная + статическая карточка) коде:

  • AC4 (свидетель #532, smoke_daycycle_raster.mjs) — лично прогнан: ratio: 1.55 при потолке 2 (pairedRatios: [1.53, 1.59, 1.55]), staticMedianMs: 138.4, dayCycleMedianMs: 211.2. Совпадает с числом, заявленным автором.
  • AC6 (4 golden-сцены day-cycle) — лично прогнан полный npm run golden:verify (политика demo/golden/policy.mjs запрещает частичный --scenario в режиме verify, только полная матрица): 172/172 passed, включая day-cycle-dawn-dark, day-cycle-day-dark, day-cycle-dusk-dark, day-cycle-night-dark. git status после прогона чист — новых/изменённых файлов в demo/golden/baselines/** нет.

Что проверено и корректно

  • Дисциплина «тест умеет падать» применена к обоим новым мутантам (daycycle-static-outline-promoted-on-inner-paper, daycycle-programmatic-camera-skips-safe-outline) лично, не на слово автора: оба поймали свой мутант 1/1 через node scripts/mutation-gate.mjs --id=....
  • Структурный тест test/paper-scene-contract.test.mjs обновлён содержательно, а не ослаблен: старое assert.doesNotMatch(staticRender, /class="hp-paper-outline-svg"/) (r1/r2-архитектура — статика без контура) заменено на assert.match(...) того же паттерна плюс новая проверка, что _startCameraTransition содержит вызов _activateSafeDayCycleOutline() перед _cameraTransition.start. Лично прогнан: node --test test/paper-scene-contract.test.mjs — 2/2 ok.
  • CSS не конфликтует: .hp-static-stage не несёt класс .stage (src/space-render.ts:898), поэтому общий селектор .stage.daycycle .hp-paperg статическую карточку не затрагивает — правило filter: none для .hp-static-stage.daycycle .hp-paperg не соревнуется по специфичности с чужим правилом, оно просто в другом поддереве. .hp-static-stage > .hp-paper-outline-svg { z-index: 0; overflow: visible; } в src/space-card.ts дублирует (не переопределяет иначе) общее правило .hp-paper-outline-svg из plan.styles.ts теми же значениями.
  • Общий helper (AC7): и полная карточка (houseplan-card.ts:11598-11603, 11626), и статическая (space-render.ts:902-906,915) строят контур и видимую бумагу через один и тот же renderPaperShapes(paperShapes, ...) — не два похожих, а один код.
  • Целевые смоки по дельте — выбраны node scripts/smoke-select.mjs --base 226f3230 --head 54dc836d (категория «зарегистрированная связь», символ renderPaperShapes, 5 смоков). Все 5 лично прогнаны и зелёные: smoke_daycycle_layer_budget.mjs, smoke_smooth_zoom.mjs (programmaticCameraActivatesSafeDayCycleOutline: true), smoke_bg_color.mjs (staticCardLayersStayOrdered: true), smoke_daycycle_raster.mjs, smoke_live_pan_coverage.mjs.
  • Typecheck/build/bundle — лично прогнаны (не только по ссылке на Validate): npm run build (tsc --noEmit && rollup) без ошибок, node scripts/bundle-sync.mjs разложил три копии бандла, git status после этого чист (собранное байт-в-байт совпадает с закоммиченным на этом SHA). npm run bundle:budget: initial View: 291143 B gzip при потолке 291700 ±2000 — в бюджете (совпадает с числом автора 291162, разница — шум сборки). Предупреждение о низком запасе (headroom 9923 Б < 15000) — предсуществующий долг (#367/#474/#499), этой задачей не создан и не увеличен заметно.
  • node scripts/check-docs.mjs — лично прогнан, Documentation checks passed (7 files, 12 external links); отпечаток docs/images/screenshots.json в дельте обновлён (sourceFingerprint пересчитан), что и требуется при любой правке src/**.
  • Документация (ARCHITECTURE.md, SUN.md, TESTING.md) в дельте описывает именно новую архитектуру — «static space card uses the same stage-sized day-cycle outline from its first frame», а не старое «has no camera gesture … keeps its historical inner outline», которое было источником находки r2. Текст согласован с кодом, а не переписан частично.
  • Трейлеры/changelog: 54dc836d несёт Issue: #582, User-Visible: no — корректно, т.к. запись в оба changelog уже сделана в a68e4c6b (первый user-visible фикс задачи) и описывает финальное видимое поведение («outline appearance and static backgrounds are unchanged»); эта дельта не добавляет нового видимого поведения, только достраивает механизм под капотом для двух путей, которые r2 поймал незакрытыми.

Чего не проверял и почему

  • Полный npm test (2714+ юнитов) отдельно не перегонялся — Validate на точном SHA 54dc836d зелёный (https://github.com/Matysh/houseplan-card/actions/runs/34959723812), включает npm test целиком; из затронутого дельтой юнит-теста прогнан адресно (test/paper-scene-contract.test.mjs, 2/2).
  • npx tsc --noEmit отдельно не гонялся — покрыт тем же Validate-прогоном и тем же npm run build, который выполнялся лично.
  • Полный npm run golden:verify — вопреки общему правилу «дорогие гейты не перегонять, если уже зелёные на SHA», был лично прогнан целиком, т.к. политика верификации (demo/golden/policy.mjs) не допускает частичный прогон в режиме verify, а диф правки трогает рендер. 172/172 passed.
  • Полная матрица demo/smoke_*.mjs (250 файлов) не прогонялась — вне скоупа делты, прогон всех уместен только когда задача трогает всё; выбор ограничен инструментом smoke-select.mjs (5 файлов, все прогнаны).
  • npm run invariants — не запускался: геометрия комнат/рёбер/толщины стен/layout/marker.space/open_spans дельтой не затронута.
  • python -m pytest tests_backend — не запускался: дельта не трогает custom_components/**/*.py.
  • Perf-профили вне #532-свидетеля (npm run benchmark:*) — не названы в AC и не затронуты дельтой, не гонялись.
  • AC9 (полевая приёмка в HA Companion) — по определению вне автоматических ворот код-ревью, остаётся владельцу в следующей бете.
  • «Одно число — один источник» — задача не добавляет и не меняет ни одной видимой пользователю величины (числа, подписи, площади); проверка неприменима.

Находки

Находок нет. Оба блокирующих High/Medium из r2 закрыты предметно в скоупе задачи, подтверждены личным прогоном тестов и мутантов (не на слово автора), делта не вносит новых регрессий в переподтверждённые AC4/AC6.

Вердикт

Зелёный. High: 0. Medium: 0.


Материал раунда

  • Ветка: issue/582-webview-large-filter-layers, коммит 54dc836deec3 — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
  • Дерево материала: 767e57c30645bb92213f4cc67e4179bdca734646
    git log --all --format='%H %T' | grep 767e57c30645
    
  • Тело issue: 4c1e1ecfe8ce205f3cfb3c44eb6028a35d0bb4358323e05ba60c6fd85b3b40ba
  • Вердикт конвейера: green · High 0