Files
houseplan-card/docs/reviews/CODE-REVIEW-307-r1.md
T
2026-08-25 17:09:10 +00:00

172 lines
15 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-307-r1
Issue: #307 · «Рисование стен: осевые линии и узлы не видны на уже поставленных
сегментах цепочки до её завершения» · трек `small` (ТЗ в теле issue) · заход r1
(первый цикл) · бюджет циклов лёгкого трека 0/2 израсходовано.
Ветка приведена к `dev` конвейером до ревью (893a9aa0 → 11343454, +1 коммит
dev). Разбор полный (это первый заход, вопрос «дельта vs полный разбор» из
§2.10 к r1 не применяется).
Коммит под ревью: `11343454` — `fix: keep the active chain axis and nodes
above the wall bodies (#307)`. Единственный коммит в диапазоне
`origin/dev..HEAD`.
## Скоуп
Симптом: во время рисования цепочки стен (Plan → «Стены») уже поставленные
сегменты не показывают осевую линию и узлы — их закрашивает непрозрачное тело
свежеперсистнутого драфта. Видна только резинка (`.active-axis`/
`.active-vertex`) активного отрезка. Причина зафиксирована в issue двумя
пунктами: (1) `.pathline`/`.vertex` рисовались в `_renderMarkupLayer`, который
вставлен в композицию **до** `_renderWallBodies`; (2) снап-оверлей рисуется
**после** тел, но намеренно исключает активный драфт из геометрии — это
осознанное решение, не трогается.
Заявленное решение (вариант A из issue): вынести разметку активной цепочки в
отдельный editor-слой, вставленный **после** тел стен и **до** снап-оверлея.
Соответствует Core user job из `docs/SCOPE.md`: J6 «Keep the plan true as the
home evolves» (два редактора, drag/resize) — сам инструмент рисования стен;
чинится визуальная обратная связь редактора, не продуктовая функция. Вне
скоупа продукта задача не выходит: чисто editor-only правка рендеринга,
никакого нового поведения, конфига или контракта.
## Как проверялось
| Гейт | Результат | Команда |
|---|---|---|
| typecheck | зелёный, без вывода | `npx tsc --noEmit` |
| unit-тесты | 1304 pass / 0 fail / 1 skipped | `npm test` |
| build | собран, 14 с | `npm run build` |
| сверка 3 копий бандла | идентичны после пересборки (`md5sum` совпал с закоммиченными `dist/` и `custom_components/.../frontend/`, `bundle:sync` пересоздал недостающую `demo/srv/assets/` копию) | `npm run build && npm run bundle:sync` |
| docs (`docs` job) | зелёный, отпечаток скриншотов совпал | `node scripts/check-docs.mjs` → «Documentation checks passed (7 files, 10 external links)» |
| новый смок `demo/smoke_active_chain_ink.mjs` | **OK** (пиксельные пробы `axisMidYellow: true`, `nodeYellow: true`, RGB `[255,193,77]`/`[129,87,23]`) | `node demo/smoke_active_chain_ink.mjs` |
| тот же смок на коде **до** фикса (`HEAD~1:src/houseplan-card.ts`, пересобран) | **падает**: `inkAboveBodies: false`, `axisMidYellow: false`, `nodeYellow: false` (пиксели `[255,255,255]` — фон вместо оси) — тест умеет ловить регрессию | `node demo/smoke_active_chain_ink.mjs` на откаченном src, дерево затем полностью восстановлено до `HEAD` |
| выборка смежных смоков (`smoke-select.mjs`, 14 прямых совпадений по `_path`/`_cursorPt`/`_markup`/`_contourClosed`/`Axis`) | все 14 — **OK** | см. список ниже |
| `npm run golden:verify` (полная матрица, 127 сценариев) | **все `passed`**, включая 4 сценария из группы риска | `npm run golden:verify` |
| `npm run invariants` | не прогонялся — не применимо, см. «Чего не проверял» | — |
| `python -m pytest tests_backend` | не прогонялся — diff не касается `custom_components/**/*.py` | — |
Прямые совпадения `smoke-select.mjs`, прогнанные все 14 — все **OK**:
`smoke_draw_wall_thickness`, `smoke_align_guides`, `smoke_editor_gestures`,
`smoke_plan_drawing_repairs`, `smoke_plan_snap_overlay` (AC3 явно требует его),
`smoke_room_autoclose`, `smoke_unified_wall_tool`, `smoke_wall_junctions`,
`smoke_edit_walk`, `smoke_merge_split`, `smoke_optimize_coincident_partition`,
`smoke_resize_audit_1550`, `smoke_room_resize`, `smoke_split_nonsnap`.
«Слабую связь» (18 смоков, одно распространённое имя `_path`/`_cursorPt`) не
гонял — ни один не выглядит специфичным к слоям композиции плана, а прямые
совпадения уже покрывают draw/junction/snap/resize/merge-split поверхность.
`npm run golden:verify` запускался несмотря на то, что ТЗ его не называет,
по прямому предписанию процесса: diff — это переупорядочивание editor-only
SVG-слоёв (изменился z-order `.pathline`/`.vertex`/`.active-axis`/
`.active-vertex` относительно тел стен), то есть «может изменить видимый
результат: слои». Нашёл 4 golden-сценария в plan-режиме, которые выставляют
`card._path`/`_tool='draw'` поверх настоящей геометрии стен
(`plan-snap-endpoint-light`, `plan-snap-line-gaps-dark`,
`wall-junctions-plan-preview-light`, `wall-junctions-plan-t-dark` —
`demo/golden/matrix.mjs:266-285`, `demo/golden/harness.mjs:965-1017`) — это
ровно тот класс состояния (резинка/поставленные точки цепочки поверх
реальных стен), для которого правка меняет порядок отрисовки. Полная матрица
прошла зелёной без единого расхождения — риск проверен и снят, а не просто
предположен.
## Находки
Отсутствуют. High: 0, Medium: 0, Low: 0.
## Что проверено и корректно
**AC1 (ось и узлы видны на поставленных сегментах).** Доказано автотестом
(`demo/smoke_active_chain_ink.mjs`), тест умеет падать — прогнан на коде до
фикса, три проверки красные с ожидаемым фоновым цветом вместо жёлтого. На
текущем коде все проверки зелёные, включая точные RGB-пробы в середине оси
первого сегмента и в узле.
**AC2 (слой цепочки между телами и снап-оверлеем, резинка не регрессирует).**
Доказано чтением кода и тем же смоком:
- `src/houseplan-card.ts:17565` (`_renderWallBodies`) →
`:17585-17586` (новый `<g class="hp-editor-only-layer">` вокруг
`_renderActiveChainInk()`) → `:17587-17588` (`_renderPlanSnapOverlay`) —
порядок в композиции совпадает с контрактом ТЗ «после тел, до снап-оверлея».
- Смок проверяет тот же факт через `compareDocumentPosition` в DOM
(`inkAboveBodies`, `inkBelowSnapOverlay`) — оба true.
- Резинка (`.active-axis`/`.active-vertex`) перенесена в тот же новый метод
`_renderActiveChainInk` (`:20057-20074`) без изменения условий рендера —
идентичный JSX-блок, только другое место вставки; смок подтверждает
присутствие обоих элементов при активном курсоре.
- Инструмент «Split» использует свои собственные `.pathline`/`.vertex` внутри
`_renderMarkupLayer` (`:20033-20040`) — их ТЗ явно оставляет на месте, и
диф их не трогает: отдельный блок кода, не задет переносом.
**AC3 (снап не меняется).** `src/plan-snap-overlay.ts` не входит в diff
(проверено — пустой `git diff` по файлу). `smoke_plan_snap_overlay` зелёный.
Существующие юнит-тесты снапа входят в общий зелёный прогон `npm test` (1305
тестов, ни один не упал). Проверено чтением + существующим смоком, как и
требует AC.
**CSS не завязан на структуру родителя.** `.pathline`, `.vertex`,
`.active-axis`, `.active-vertex` — глобальные селекторы классов в
`src/styles.ts:1803-1863`, не скоупятся через потомство конкретного `<g>`.
Перенос элементов в новый `<g class="hp-editor-only-layer">` (тот же паттерн,
что уже используют align-guides, hidden-wall-diagnostics,
opening-placement-preview, снап-оверлей — `opacity="${modeVisual?.editorWeight
?? 1}"`) не требует правок стилей и не ломает `editorWeight`-затемнение в
неактивных режимах.
**Трейлеры и changelog.** Единственный коммит несёт `Issue: #307` и
`User-Visible: yes`; оба `docs/CHANGELOG.md`/`docs/CHANGELOG.ru.md` правлены в
том же коммите (проверено `git show --stat`, `git diff` по обоим файлам —
согласованные формулировки на двух языках, ссылка на issue).
**Формулировка ТЗ vs UI.** ТЗ цитирует `docs/USER-GUIDE.ru.md:372-374`
дословно («поверх уже нарисованных стен видны тонкие осевые линии и точки их
концов») — сверено, совпадает буквально.
**Откат.** Один коммит, без новых полей конфига, миграций или
persisted-состояний — подтверждено диффстатом (только `src/houseplan-card.ts`
+ сгенерированные копии + смок + changelog + отпечаток скриншотов).
**«Одно число — один источник».** Не применимо: диф не добавляет и не
меняет ни одной пользователю видимой величины (числа, площади, подписи) —
только z-order editor-only SVG-элементов. `test/single-source-numbers.test.mjs`
входит в общий зелёный `npm test`.
## Чего не проверял
- **`npm run invariants`** — не прогонял. Diff не касается геометрической
модели: нет правок `layout`, записей толщины, `marker.space`, `open_spans`,
`physical-geometry.ts`, `plan-snap-overlay.ts`. Изменение — это
переупорядочивание editor-only SVG-слоёв внутри одного компонента
рендеринга; ни одна из трёх инвариантных проверок (запись толщины,
разрешимость ссылок, ключ записи = ключ решёточного ребра) не имеет
отношения к этому диффу.
- **`python -m pytest tests_backend`** — не прогонял. Diff не касается
`custom_components/houseplan/**/*.py` (только сгенерированная копия фронтенд-
бандла в `custom_components/houseplan/frontend/houseplan-card.js`, класс D).
- **Полный набор `demo/smoke_*.mjs`** — не гонял все ~70+ смоков, только 1
новый + 14 прямых совпадений (см. таблицу и обоснование выше). Слабые связи
(18 смоков с одним общим именем `_path`/`_cursorPt`) не прогонял — по
выводу инструмента это решение ревьюера, и просмотр списка (drag/pan/split/
opening-preview на разных, не связанных с композицией плана поверхностях)
не выявил кандидатов, специфичных именно к порядку SVG-слоёв.
- **`performance_smoke`** — не прогонял. Не назван в AC, диф не трогает
чувствительные к перфу пути (рендер не добавляет новых вычислений — тот же
шаблон переставлен в другое место композиции, число DOM-узлов не меняется).
- **Ручное тестирование в браузере** — не проводилось (в цикле code-review
ручного тестирования нет по регламенту); функциональность подтверждена
смоком с пиксельными пробами и golden-верификацией композитного рендера.
## Вердикт
Все 3 AC доказаны: AC1/AC2 — автотестом, который умеет падать (проверено на
коде до фикса), AC3 — чтением кода (файл снапа вне диффа) плюс существующим
зелёным смоком. Дешёвые гейты (typecheck/test/build/bundle-sync/docs) зелёные.
Прямые смежные смоки (14) зелёные. Полная golden-матрица (127 сценариев,
включая 4 сценария из группы прямого риска — резинка/точки цепочки поверх
настоящих стен в plan-режиме) зелёная без единого расхождения. Трейлеры и оба
changelog корректны. Находок нет.
**Зелёный.**