mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-28 19:01:34 +00:00
@@ -0,0 +1,226 @@
|
||||
# CODE-REVIEW-689-r1
|
||||
|
||||
Issue: #689 «Размытие плана после зума и белое мигание при навигации на дне/ночи (день/ночь) — откат #685»
|
||||
Материал: `f2b66ad67758aa6ee391b6bb14d1f803fd93eedb` (ветка `issue/689-daycycle-layers`)
|
||||
Заход: r1 · блокирующих циклов израсходовано 0 из 4
|
||||
|
||||
## Скоуп
|
||||
|
||||
Диапазон `origin/dev..HEAD` (`c1e05e46` → `f2b66ad6`), 4 коммита, 51 файл:
|
||||
|
||||
- `600330e1` fix: `.plan-svg` в день/ночь получает `will-change: opacity` вместо
|
||||
`transform` (не фиксирует растр); `.hp-paper-outline-svg` полноразмерной карточки —
|
||||
`overflow: hidden` и метка `data-hp-live-overflow="clip"`; `live-viewport.ts`
|
||||
ограничивает экспозицию `[data-hp-live-viewbox]` инлайн `clip-path: inset(-25%)`,
|
||||
снимаемым вместе с `overflow`; откат штриховки #685 (`linearGradient` → единственный
|
||||
`<pattern>`). `User-Visible: yes`, оба CHANGELOG в этом же коммите.
|
||||
- `87195c2b` — тесты/смоуки под новую композицию, `Release: v1.78.0-beta.9`.
|
||||
- `836ef771` — приёмка 29 golden-кадров из Linux-артефакта CI, `Baseline-Reviewed`.
|
||||
- `f2b66ad6` — отпечаток скриншотов документации (пиксели не менялись).
|
||||
|
||||
Продуктовые файлы: `src/styles/plan.styles.ts`, `src/live-viewport.ts`,
|
||||
`src/houseplan-card.ts` (+12/-12 строк, откат штриховки внутри `render()`).
|
||||
Трейлеры `Issue:`/`User-Visible:` на месте на каждом коммите; `Release:` появляется
|
||||
с `Baseline-Reviewed` на golden-коммите, как требует §10.1.
|
||||
|
||||
## Как проверялось
|
||||
|
||||
**Дешёвые гейты — не перегонял, зачтён Validate на этом SHA.** Run
|
||||
[36453600665](https://github.com/Matysh/houseplan-card/actions/runs/36453600665) —
|
||||
`success`, но это workflow_dispatch с `heavy=false`: реально исполнились и прошли
|
||||
только «Предполёт» (docs/check-docs/провенанс), «Фронтенд: типы, юниты, мутанты,
|
||||
синхрон бандла» (= `tsc --noEmit`, `npm test`, `npm run build` + `bundle-policy
|
||||
--verify`) и «Мутанты по диффу» (6/6). Golden/смоки/perf/geometry/backend в этом
|
||||
прогоне **skipped**, не «переиспользованы» — я проверил это по логам job
|
||||
«Переиспользование: это дерево уже проверено»: `smoke`/`golden` там нашли кэш-маркер
|
||||
именно с `source_run=36440159452` (тот же SHA), а `performance_smoke` — «Cache not
|
||||
found» (в 36440159452 перф красный, маркер успеха не пишется), и сам job
|
||||
`performance_smoke` не запустился вовсе, потому что `needs.changes.outputs.heavy`
|
||||
в этом workflow_dispatch оказался `false` — это НЕ доказательство перфа, только
|
||||
подтверждение tsc/test/build/mutants-by-diff. Авторитетное golden+smoke evidence для
|
||||
этого SHA — прогон [36440159452](https://github.com/Matysh/houseplan-card/actions/runs/36440159452)
|
||||
(красный только перф, остальное зелёное).
|
||||
|
||||
**Гейты, прогнанные мной лично на этом SHA:**
|
||||
|
||||
| Гейт | Команда | Результат |
|
||||
|---|---|---|
|
||||
| Node-юниты по диффу | `node --test test/daycycle-layers.test.mjs test/live-viewport.test.mjs test/paper-scene-contract.test.mjs test/core-file-budget.test.mjs` | 24/24 pass; `core-file-budget`: `houseplan-card.ts` 12890 строк ≤ потолка 12891 |
|
||||
| Мутанты (AC7), каждый лично применён и погашен через `mutation-gate.mjs --id=` | `daycycle-scene-transform-hint-back`, `daycycle-outline-overflow-visible`, `live-exposure-unbounded`, `live-exposure-opens-clipped-scene`, `hatch-stroke-not-scaled`, `hatch-zoom-compensation-back` | 6/6 — «заявленный тест покраснел на мутанте» |
|
||||
| Смоки, названные в АС/ТЗ (после `npm run build && bundle-sync`) | `smoke_daycycle_zoom_layers`, `smoke_daycycle_layer_budget`, `smoke_static_zoom_sharpness`, `smoke_wall_hatch_density`, `smoke_daycycle_raster`, `smoke_live_pan_coverage` | 6/6 `OK`, все внутренние проверки `true` |
|
||||
| `npm run golden:verify` (полный набор, не только сдвинутые) | — | **188/188 passed, 0 failed** |
|
||||
| `node scripts/smoke-select.mjs --base origin/dev --head HEAD` | — | 0 прямых/зарегистрированных совпадений, 26 «слабых» по `_zoom` (см. таблицу выше — все явно названные в ТЗ прогнаны), НЕОПРЕДЕЛЁННОСТЬ по `HATCH_BASE_STEP_UNITS`, `LIVE_SCENE_EXPOSURE_CLIP`, `SCENE_OVERFLOW_ATTRIBUTE` и др. — все они прямо покрыты новым `test/daycycle-layers.test.mjs`/`test/live-viewport.test.mjs` и смоками из строки выше |
|
||||
| `git diff` полноразмерных `<pattern>`/`linearGradient` по всему дереву | `grep -rn linearGradient src demo test` | ни одного `linearGradient#hp-wall-hatch` не осталось (полный откат #685 подтверждён) |
|
||||
| Docs-фингерпринт скриншотов | сверка `imageSha256` в `docs/images/screenshots.json` построчно | не изменился ни один из 11, только `sourceFingerprint`/`sourceSha256` — «pixels unchanged» подтверждено |
|
||||
|
||||
**Perf (AC6) — не зелёный на точном SHA, разобрано отдельно ниже.**
|
||||
|
||||
**Чего не проверял:** `python -m pytest tests_backend` (диф не касается
|
||||
`custom_components/**/*.py`), `npm run invariants` (геометрия стен/проёмов как данные
|
||||
не тронута — тронут только способ растеризации и композиция слоёв), полный `npm test`
|
||||
(3210+, отдельной командой) — положился на зелёный job «Фронтенд» на этом SHA
|
||||
(36440159452 и повторно 36453600665), ручную визуальную приёмку 29 golden-кадров
|
||||
глазами — доверился `Baseline-Reviewed` трейлеру и Linux-артефакту CI, как
|
||||
предписывает процесс для этого гейта. HA Companion и «встроенный браузер» реального
|
||||
устройства не проверял — недоступно в среде ревью; для AC1/AC4 это оставлено как
|
||||
визуальное свидетельство владельца (сам автор пометил их так же: «НЕ сделано»).
|
||||
|
||||
## По контракту и AC
|
||||
|
||||
- **AC1 (нет зафиксированного растра).** `test/daycycle-layers.test.mjs`,
|
||||
`smoke_daycycle_zoom_layers`, `smoke_daycycle_raster`, `smoke_daycycle_layer_budget`
|
||||
— везде `will-change: opacity` после осевшего кадра, `will-change: transform`
|
||||
нигде не остаётся. Прочитан код: `.stage.daycycle.hp-safe-daycycle-outline
|
||||
.plan-svg` (`plan.styles.ts:122`) — единственное место, где раньше стоял
|
||||
`transform`. Владелец подтвердил в реальном Chrome (issue-комментарий), что
|
||||
пробный CSS даёт чёткий кадр; полная бандл-версия владельцем ещё не
|
||||
перепроверена (сам автор фиксирует это как «не сделано») — доказательство
|
||||
контракта в CI/юнитах полное, финальная визуальная приёмка вне headless
|
||||
остаётся открытой по объективной причине (сам продукт задачи).
|
||||
- **AC2 (бюджет слоёв на 800%).** `smoke_daycycle_zoom_layers` (CDP LayerTree, DPR 2,
|
||||
день/ночь, тёмная заливка, колесо+кнопки+pinch+перезагрузка+навигация) — прогнан
|
||||
лично: `gestureLayersWithinBudget` (≤1.5× сцены), `settledLayersWithinBudget`
|
||||
(≤1× сцены + 240px), `noWhiteFrames` (screencast, порог 0.25 доли почти-белых
|
||||
пикселей) — все `true`. Пороги смока строже ТЗ (1.5×/1× против 2.5×/1.5×).
|
||||
- **AC3 (экспозиция ограничена и снимается).** `test/live-viewport.test.mjs` — два
|
||||
новых теста: инлайн `overflow: visible` + `clip-path: inset(-25%)` во время
|
||||
проекции, оба свойства уходят при терминальном кадре (idle-DOM совпадает с
|
||||
состоянием до жеста), сцена с `data-hp-live-overflow="clip"` никогда не
|
||||
экспонируется. Прогнано лично, 2/2 pass; оба мутанта, которые снимают эту защиту
|
||||
(`live-exposure-unbounded`, `live-exposure-opens-clipped-scene`), лично применены
|
||||
и погашены смоком/юнитом.
|
||||
- **AC4 (нет белых кадров).** `smoke_daycycle_zoom_layers.noWhiteFrames` — 0 почти-белых
|
||||
кадров в центральной выборке после перезагрузки на 800% и навигации колесом.
|
||||
Владелец подтвердил в реальном Chrome 152 (issue). Как и AC1, финальная
|
||||
бандл-проверка владельцем на полном билде — открытый пункт по признанию автора,
|
||||
не находка ревью (headless объективно не видит симптом, это прямо оговорено в ТЗ).
|
||||
- **AC5 (откат #685).** `smoke_wall_hatch_density` (10/10) и `smoke_static_zoom_sharpness`
|
||||
(все режимы) проверяют единственный `<pattern id="hp-wall-hatch">`; `grep` по всему
|
||||
дереву не находит `linearGradient#hp-wall-hatch`. `golden:verify` 188/188, включая
|
||||
21 `junction-*`/`large-house-zoom-0{40,250}` кадр, вернувшийся к виду до `139dceaa`,
|
||||
и 4 `static-hatch-openings-*` с восстановленным паттерном. 4 кадра `stairs-*` из
|
||||
#688 приняты тем же коммитом с явной атрибуцией и ссылкой на #690 — не скрытое
|
||||
поглощение чужого дифа, а зафиксированный долг по рекомендации самого #690.
|
||||
- **AC7 (защитные мутанты).** 6/6 лично погашены (таблица выше); реестр браузерных
|
||||
гвардов остаётся на потолке 200/200 (одна запись удалена, одна добавлена — `grep -c`
|
||||
подтверждает).
|
||||
- **К6 (idle-DOM).** `smoke_live_pan_coverage` обновлён: экспонированная сцена
|
||||
открывается только в рамках `clip-path`, помеченная `clip` сцена не открывается
|
||||
никогда, снятие включает и `clipPath`. День/ночь golden не сдвинулись нигде, кроме
|
||||
мест, которые меняет К1/К3/К5 по самому ТЗ.
|
||||
- **Не-скоуп соблюдён.** `.hp-static-stage` (пространственная статичная карточка,
|
||||
`space-card.ts`/`space-render.ts`) — отдельный компонент с собственными стилями;
|
||||
правило `.stage .hp-paper-outline-svg { overflow: hidden }` на неё не действует
|
||||
(разная специфичность/разный корневой класс, не пересекаются) — прочитано и
|
||||
подтверждено, `.hp-paperg`-композиция (нетронутая карточка) не менялась.
|
||||
|
||||
### AC6 (перформанс) — разобрано отдельно, вердикт ревьюера: не блокирует
|
||||
|
||||
Точный SHA #689 на авторитетном Linux CI-прогоне
|
||||
([36440159452](https://github.com/Matysh/houseplan-card/actions/runs/36440159452))
|
||||
красный ровно на одной метрике из 51:
|
||||
|
||||
```
|
||||
❌ timing.firstStableRenderMs.median 3002.4 / 3000 (+2.4 мс, +0.08 %)
|
||||
```
|
||||
|
||||
`hardMaxMs` в `evaluate.mjs` — абсолютный потолок (`Math.min(hardMaxMs,
|
||||
regressionLimit)`), не смягчается `noiseAllowanceMs`; формально AC6 не доказан
|
||||
зелёным прогоном на этом SHA. Автор сам не прогнал его повторно (последующий
|
||||
«зелёный» Validate 36453600665 не покрывает perf — см. «Как проверялось» выше) и
|
||||
явно передал вывод ревьюеру («вывод о бюджете firstStableRenderMs после #691 — за
|
||||
ревьюером»).
|
||||
|
||||
Я разобрал вопрос по коду и данным, а не принял решение владельца/автора на слово:
|
||||
|
||||
1. **Профиль не исполняет код #689.** `large-house-interaction-v1` не включает
|
||||
день/ночь — `.stage.daycycle...`-правило и `.hp-paper-outline-svg`-клип не
|
||||
участвуют вовсе (элемент не рендерится без `dayCycle`). Единственная
|
||||
всегда-активная часть диффа — экспозиция `clip-path` в `setLayerProjection` —
|
||||
исполняется во время `panSeriesMs`/`cameraSeriesMs`; оба прошли с большим
|
||||
запасом (433.2/500, 199.9/500) — не похоже, что механизм экспозиции заметно
|
||||
давит именно на `firstStableRenderMs` (окно, которое гейтов жестов не включает).
|
||||
2. **#691, после которого впервые измерен объединённый `dev`, докс-онли** — не мог
|
||||
изменить рантайм-код. Значит превышение — свойство текущего `dev` HEAD,
|
||||
впервые измеренное на этом прогоне: `performance_smoke` у #691 самого не
|
||||
запускался ни разу (`heavy=false` во всех 4 прогонах его ветки).
|
||||
3. **Локальный A/B, прогнанный лично** (одна и та же песочница, `--samples=3
|
||||
--warmups=1`, диагностика, не гейт-evidence): `dev` @ `c1e05e46` — 2936.0 /
|
||||
2926.8 / 2961.1; ветка @ `f2b66ad6` — 2961.0 / 2939.4 / 2962.0. Разница между
|
||||
ветками (~25 мс) того же порядка, что разброс сэмплов внутри одного `dev`
|
||||
(~34 мс) — на трёх сэмплах отличить регрессию от шума нельзя.
|
||||
|
||||
Вывод: превышение бюджета не приписывается диффу #689 — правдоподобнее, что
|
||||
`large-house-interaction-v1`/`firstStableRenderMs` уже стоит на пределе `hardMaxMs`
|
||||
у текущего `dev` и это впервые проявилось именно на этом рибейзе. Это Medium-находка
|
||||
вне скоупа #689 (соседний, общий perf-бюджет, а не поведение задачи) — заведена
|
||||
отдельно: **[#692](https://github.com/Matysh/houseplan-card/issues/692)**
|
||||
«Перф-гейт: large-house-interaction-v1 firstStableRenderMs на пределе hardMaxMs=3000
|
||||
независимо от #689», с полным разбором и предложенным планом (авторитетный 7-сэмпловый
|
||||
прогон `dev` HEAD, решение — расширять бюджет или чинить деградацию).
|
||||
|
||||
## Число, видимое дважды
|
||||
|
||||
`HATCH_BASE_STEP_UNITS = 8` — используется и в `wall-thickness.ts` (шаг штриховки
|
||||
на эталонной клетке), и в новом `stripe = 2 * (hatchStep / HATCH_BASE_STEP_UNITS)`
|
||||
(`houseplan-card.ts`). Один источник — константа импортирована, не задублирована
|
||||
числом; проверено алгебраически: на `cell_cm=5` (`hatchStep=8`) `stripe = 2`, на
|
||||
`cell_cm=25` (`hatchStep=1.6`) `stripe = 0.4` — совпадает с historical `stroke-width`
|
||||
и с `smoke_wall_hatch_density` (`referenceStrokeIsTwo`, `coarseGridScalesTheStroke`).
|
||||
|
||||
## Что проверено и корректно
|
||||
|
||||
- Откат #685 полный и чистый: ни одного `linearGradient#hp-wall-hatch` не осталось
|
||||
нигде в дереве; штриховка регрессионно идентична допо-#685 состоянию (golden
|
||||
21/21 кадр вернулся к виду до `139dceaa`).
|
||||
- Композиция слоёв день/ночь и статичной полноразмерной карточки читается и
|
||||
исполняется согласованно с ТЗ К1–К4, К6, К7; не-скоуп (`.hp-static-stage`,
|
||||
`.hp-paperg`) не задет — прочитано и подтверждено отдельно.
|
||||
- Все 6 заявленных мутантов лично применены к рабочей копии и погашены
|
||||
соответствующим гейтом — не принято на слово из хендоффа.
|
||||
- Golden (188/188) и все явно названные в ТЗ/АС смоки лично прогнаны и зелёные.
|
||||
- Трейлеры, changelog (оба языка, в одном коммите с `User-Visible: yes`), канонические
|
||||
документы (`SUN.md`, `ARCHITECTURE.md`, `WALL-THICKNESS.md`) — обновлены точно там,
|
||||
где меняется контракт, лишнего/недостающего не найдено.
|
||||
- `core-file-budget` потолок `houseplan-card.ts` не поднят (12890 ≤ 12891) — решение
|
||||
«инлайн вместо метода» не увеличивает монолит, при этом сохраняет откат #685
|
||||
буквально (атрибуты паттерна, запасной цвет).
|
||||
- Golden-долг #688 (`stairs-*`) явно назван в коммите, не поглощён молча — соответствует
|
||||
собственной рекомендации #690 п.3.
|
||||
|
||||
## Находки
|
||||
|
||||
**Medium (вне скоупа #689) — заведён отдельный issue, не блокирует.**
|
||||
|
||||
- `large-house-interaction-v1`/`firstStableRenderMs` красный на точном SHA (+0.08 % над
|
||||
`hardMaxMs`); по анализу выше — свойство `dev`, не этой задачи → **#692**.
|
||||
|
||||
High: 0. Medium в скоупе: 0. Low: 0.
|
||||
|
||||
## Итог
|
||||
|
||||
AC1–AC5, AC7 доказаны исполнением (юниты, смоки, golden, мутанты — лично прогнаны, не
|
||||
приняты на веру из хендоффа); AC1/AC4 сохраняют объективно открытым только финальный
|
||||
шаг «владелец смотрит на полный бандл в реальном браузере/Companion» — то же
|
||||
ограничение, что и в самой задаче (headless не видит симптом), не находка ревью.
|
||||
AC6 красный на точном SHA, но по независимому разбору кода и диагностическому A/B —
|
||||
не является следствием диффа #689; вынесен отдельным issue #692 согласно правилу
|
||||
«Medium вне скоупа». Задача решает заявленный сценарий (резкость после зума,
|
||||
отсутствие мигания, откат #685) без регрессии в touch-контракте, бюджете монолита или
|
||||
golden-корпусе.
|
||||
|
||||
**Вердикт: зелёный.**
|
||||
|
||||
---
|
||||
|
||||
<!-- material-anchors: сгенерировано конвейером (#414) -->
|
||||
|
||||
## Материал раунда
|
||||
|
||||
- Ветка: `issue/689-daycycle-layers`, коммит `f2b66ad67758` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет.
|
||||
- Дерево материала: `a14a6fb6b30afe13f9cf7ad1b11b343be3e8a0c6`
|
||||
```
|
||||
git log --all --format='%H %T' | grep a14a6fb6b30a
|
||||
```
|
||||
- Тело issue: `57b2345003d4b8f80b2c00dcc327b4ae2832e3de6524d1ac404eb5a32d6c84ab`
|
||||
- Вердикт конвейера: `green` · High 0
|
||||
Reference in New Issue
Block a user