Files
houseplan-card/docs/reviews/CODE-REVIEW-122-r2.md
T
2026-08-13 22:27:31 +00:00

151 lines
14 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.
# Код-ревью #122 — Isometric Stage 2: hidden visual polish (r2)
- Issue: https://github.com/Matysh/houseplan-card/issues/122
- Спецификация: `docs/specs/122-isometric-stage2.md` (SPEC-REVIEW-122-r1: green)
- Предыдущий цикл: `docs/reviews/CODE-REVIEW-122-r1.md` — красный, High: 2, Medium: 1 → #134
- Диапазон: `git diff origin/dev...HEAD`, новый коммит цикла —
`ef3cc98` (`fix: preserve isometric fallback rendering`), поверх `42b3f44`
(`feat: add hidden isometric stage 2`), `76ce755`/`4c73e2c`/`e13215c` (ТЗ и
документы ревью)
- Роль: ревьюер кода (свежая сессия, без контекста реализации и без контекста
сессии r1)
- **Вердикт: зелёный · цикл r2/4 · High: 0 · Medium: 0**
## Скоуп
Единственный продуктовый коммит цикла — `ef3cc98`. Класс файлов: A
(`src/houseplan-card.ts`, 13 insertions/4 deletions — два места: `_baseVb()` и
основной `<svg>`/`<g>` блок рендера плана) + B (`test/isometric-contract.test.mjs`,
+5 строк) + C (`docs/ISOMETRIC.md`, `docs/adr/122-isometric-stage2-composition.md`)
+ D (`dist/houseplan-card.js`, `custom_components/houseplan/frontend/houseplan-card.js`,
`demo/srv/assets/houseplan-card.js` — все три пересобраны и включены в тот же
коммит). Диапазон узкий и точечный: правка адресует ровно High-1 и High-2 из r1,
`demo/smoke_isometric_contract.mjs` и `demo/smoke_isometric_live_touch.mjs` в этом
коммите не менялись (они уже были частью `42b3f44`).
Трейлеры `ef3cc98`: `Issue: #122`, `User-Visible: no` — корректно, фича
по-прежнему за скрытым Labs-флагом `iso`, публичный changelog не требуется.
## Как проверялось
| Гейт | Команда | Результат |
|---|---|---|
| Typecheck | `npx tsc --noEmit` | green, без вывода |
| Unit | `npm test` | green, 766/766 |
| Build | `npm run build` | green |
| Синхронизация 3 копий бандла | `cmp dist/houseplan-card.js custom_components/houseplan/frontend/houseplan-card.js` и то же для `demo/srv/assets/houseplan-card.js` | **обе команды green** — байт-в-байт совпадают. High-1 из r1 закрыт |
| `npm run golden:verify` (диф меняет рендер — обязателен) | после `npm run build` + `cp dist/houseplan-card.js demo/srv/assets/houseplan-card.js` | 45/50 сценариев `passed`; 5 `different` — все пять из семейства «непринятые Iso-эталоны Stage 2» (`isometric-geometry-view-dark/light`, `isometric-live-layers-dark`, `isometric-touch-kiosk-dark`, `isometric-large-warm-remount-dark`), что и предсказывал §12.3 ТЗ и r1. **`isometric-no-borders-dark` теперь `passed`** (High-2 закрыт), **`large-house-zoom-250-dark` теперь `passed`** (Medium-1/#134 регрессия исчезла тем же коммитом) |
| Повтор `golden:verify` для этих двух сценариев | тот же прогон второй раз | оба снова `passed` — детерминированно, не флейк |
| Целевые браузерные смоки (названы в AC1/AC5-AC7/AC9-AC11 и напрямую тронуты правкой `_baseVb`/рендера) | `node demo/smoke_isometric_contract.mjs`, `node demo/smoke_isometric_live_touch.mjs` (после того же build+copy) | оба green; в `live_touch` явно проверяется `noBordersUsesFloorSymbols: true` и `visibleBordersRestoreStage2: true` — оба режима show_borders исполнены в реальном DOM, не только прочитаны |
| Трейлеры коммита | `git log --format=... ef3cc98` | `Issue: #122`, `User-Visible: no` — оба присутствуют и терминальны |
| Backend | не прогонялся | правок в `custom_components/**/*.py` нет — не применимо |
| Performance (`large-house-isometric-v1`) | не прогонялся | как и в r1 — pre-beta гейт, заблокирован #124 по решению владельца, вне скоупа этого код-ревью |
**Чего не проверял и почему:** полный набор из 127 браузерных смоков — правка
цикла ограничена двумя точками в одном файле плюс правкой рендера, которую уже
покрывают целевые смоки/golden; `python -m pytest tests_backend` — нет
Python-правок; принятие golden-эталонов Stage 2 Iso — не роль ревьюера и не
требуется до pre-beta (§12.3 ТЗ, решение владельца); дифференциальный прогон на
`origin/dev` — не повторялся отдельно в этом цикле, т.к. `origin/dev` не менялся
с r1 и там же было доказано, что оба сценария чисты на dev (причинность уже
локализована в диффе задачи).
## Находки
Отсутствуют. Оба High из r1 закрыты, новых High/Medium не найдено.
## Что проверено и корректно
- **High-1 (три копии бандла)** — закрыт. `cmp` подтверждает байт-в-байт
идентичность всех трёх копий после `npm run build`; коммит `ef3cc98` содержит
пересобранные `dist/houseplan-card.js`, `custom_components/houseplan/frontend/houseplan-card.js`
и `demo/srv/assets/houseplan-card.js` внутри себя же (не отдельным коммитом) —
правило «сгенерированное в том же коммите» соблюдено.
- **High-2 (`show_borders:false` в Iso ломал пиксельный контракт)** — закрыт.
Причина по коду (`src/houseplan-card.ts:4558-4573`, `_baseVb()`): при
`!showBorders` рамка теперь строится как `projectedFrame({ rect: flat,
wallHeight: ISO_WALL_HEIGHT })` — это в точности старая Stage-1 проекция без
вклада Stage 2 floor/opening-геометрии, а не безусловный `unionRect(flat,
isoOpeningBounds())`, который ломал сцену в 42b3f44. Синхронно с этим основной
`<svg>`/внутренний `<g>` (строки ~14181-14187) теперь используют
`isoLayers?.structural` вместо `iso` как условие для класса
`iso-floor-scene`/`transform`/viewBox — то есть в ветке «iso активен, но
структура скрыта» контент рендерится без Stage-2-трансформации, тем же путём,
что и Stage 1. Доказано исполнением: `golden:verify` даёт `passed` для
`isometric-no-borders-dark` с нулевым отличием от принятого Stage-1 baseline,
повторено дважды детерминированно; смок `live_touch` отдельно подтверждает
`noBordersUsesFloorSymbols: true` в реальном DOM.
- **Атрибут `class` внешнего `<svg>`** (`class=${isoLayers?.structural ? 'plan-svg' : nothing}`,
новая связка по сравнению с прежним безусловным `class="plan-svg"`) —
прочитано и проверено: `.plan-svg { z-index: 1; }` в `src/styles.ts:472`
имеет смысл только относительно братских `.iso-underlay-svg`/`.iso-shadows-svg`/`.iso-walls-svg`,
а те сами рендерятся строго под условием `iso && isoLayers?.structural`
(`src/houseplan-card.ts:14176`, `:14394`) — то есть ровно тогда же, когда
`plan-svg` получает класс. Вне Flat и вне structural-iso конкурирующих слоёв
с явным z-index нет, поэтому потеря класса не меняет порядок отрисовки.
Единственное внешнее использование селектора `.plan-svg` — `demo/smoke_isometric_contract.mjs:49`,
и там он опрашивается только в сценарии с `show_borders:true` (структурный),
где класс присутствует. Подтверждено исполнением: ни один из 44 не-iso
golden-сценариев (Flat, редакторы, lighting, tray, split-wall и т. д.) не
показал отличий.
- **Medium-1 / #134 (регрессия `large-house-zoom-250-dark` при выключенном
Labs)** — не было целью этого коммита (правка адресует High-1/High-2), но
сценарий проходит чисто тем же коммитом. Открытая гипотеза из r1 (обёртка
`<g class=${iso ? ... }>` как источник 0.1%-регрессии) теперь снята
экспериментально: смена условия с `iso` на `isoLayers?.structural` на этой же
строке устранила и её. Issue #134 остаётся открытым (`S1-new`) — закрытие
issue не входит в роль ревьюера кода (§6 PROCESS.md), но по факту дефект,
который он описывает, воспроизводимо больше не проявляется в этом дереве;
стоит отметить это релиз-менеджеру/автору при обработке #134, а не оставлять
как забытый висящий тикет.
- **AC7 (`unit`+`smoke`+`golden`)** — выполнен: `hide_openings` не трогает
cuts/state/light (не менялось в этом коммите, покрыто существующими тестами
из `npm test`); `show_borders:false` восстанавливает Stage-1 no-volume сцену —
подтверждено golden. **AC15** (Flat и `isometric-no-borders-dark` неизменны) —
выполнен для не-iso baseline; Stage-2-Iso-эталоны намеренно не приняты на
этом этапе (§12.3 ТЗ) и остаются `different` — соответствует контракту, а не
находка. **AC16** (три копии бандла байт-в-байт + зелёные typecheck/unit/build)
— выполнен, доказано `cmp`.
- **`test/isometric-contract.test.mjs`** (+5 строк) — источниковые
regex-проверки на буквальный текст новых веток `_baseVb()` и рендера
(`if (!this._spaceDisplayForRender().showBorders)`,
`projectedFrame({ rect: flat, wallHeight: ISO_WALL_HEIGHT })`,
`preserveAspectRatio=...'xMidYMid meet' : 'none'`, и т. д.). Тест умеет
падать при откате этих строк (буквальное совпадение с исходником), но сам по
себе не проверяет визуальный результат — это делает `golden:verify`
отдельно. Стиль согласован с уже существующими source-grep проверками того
же файла (AC8/AC9/AC14 в r1). Оценка: приемлемо как дополнительная страховка,
не как единственное доказательство AC7/AC15 — реальное доказательство даёт
golden.
- **Документация** (`docs/ISOMETRIC.md`, ADR) — обновлена в том же коммите,
описывает новое поведение точно (Stage-1 projected frame возвращается в
no-borders ветке вместо union с Stage-2-геометрией); расхождений с кодом не
найдено.
## Чего не проверял
- Полные 127 браузерных смоков — правка узкая (две точки в одном файле), вне
необходимости, определяемой diff'ом; прогнаны два целевых, прямо относящихся
к затронутому коду.
- `python -m pytest tests_backend` — нет изменений в Python.
- `large-house-isometric-v1` performance — вне гейта код-ревью, заблокирован
#124 (решение владельца, повторяет позицию r1).
- Принятие golden-эталонов Stage 2 Iso (5 `different`-сценариев) — не роль
ревьюера; это ожидаемое, задокументированное в ТЗ состояние, а не находка.
- Отдельный дифференциальный прогон на `origin/dev` в этом цикле — не
повторялся: `origin/dev` не изменился с r1, где причинность уже была
локализована в диффе задачи, а не в окружении/Chromium.
## Итог
Оба блокирующих High из r1 закрыты одним точечным коммитом (`ef3cc98`):
пересборка всех трёх копий бандла восстановлена и подтверждена `cmp`
(High-1); ветвление рамки/трансформации в `show_borders:false` теперь
воспроизводит принятую Stage-1 no-volume сцену вместо объединения с
Stage-2-геометрией, что подтверждено чистым `golden:verify` дважды подряд
(High-2). Ранее заведённая Medium-находка (#134) как побочный эффект того же
исправления тоже перестала воспроизводиться в golden — issue стоит закрыть
или, как минимум, обновить при её обработке, но это не входит в роль
ревьюера кода. Новых High/Medium-находок нет. Цикл r2/4 закрывается зелёным
вердиктом.