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

15 KiB
Raw Blame History

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 корректны. Находок нет.

Зелёный.