# SPEC-REVIEW-307-r1 Issue: #307 · Этап: ТЗ (spec) · Трек: `small` · Заход: r1 · Вердикт: **зелёный** Блокирующих циклов израсходовано: 0/2 (лимит лёгкого трека, §4) ## Скоуп ТЗ (в теле issue #307, small-трек, шаблон §5: проблема · контракт · AC · откат) на исправление визуального дефекта инструмента «Стены» в редакторе плана: во время рисования цепочки уже поставленные (персистнутые) сегменты теряют осевую линию `.pathline` и узлы `.vertex`, потому что тело стены рисуется поверх них, а снап-оверлей намеренно исключает активный драфт из своей геометрии. Решение — вынести ось/узлы/rubber-band активной цепочки (`.pathline`, `.vertex`, `.active-axis`, `.active-vertex`) из `_renderMarkupLayer` в отдельный editor-слой между `_renderWallBodies` и `_renderPlanSnapOverlay`, ничего не меняя в геометрии снапа. Затронутая поверхность — Plan editor (`src/houseplan-card.ts`, один модуль), только режим `_markup`/`_editing`; View не затрагивается. Персона — Home admin (единственная, кто пользуется редакторами, `docs/SCOPE.md`), контекст — задача J4/J6 («от нуля до плана» и «поддерживать план актуальным», два встроенных редактора). Изменение чисто композиционное (порядок отрисовки уже существующих DOM-узлов), без новых полей конфига, миграций, i18n-ключей. ## Как проверялось Ревью читал ТЗ и тело/комментарий issue без устных пояснений автора, состязательно — искал, где контракт невыполним или непроверяем, а не соглашался с текстом. Каждое фактическое утверждение о коде и документации проверено чтением, а не принято на веру: 1. `docs/SCOPE.md` — подтвердил, что задача обслуживает J4/J6 и не задевает инвариант View/lock; продуктового конфликта нет. 2. `docs/USER-GUIDE.ru.md:372-375` — дословно подтверждает цитату ТЗ: «поверх уже нарисованных стен видны тонкие осевые линии и точки их концов». Значит ожидаемое поведение — не догадка автора, а уже зафиксированный контракт; исключение для «ещё не завершённой цепочки» нигде не оговорено. 3. `src/houseplan-card.ts` — построчно сверил все технические утверждения ТЗ: - `_renderMarkupLayer` (:19960) действительно эмитит `.pathline` (:20029), `.active-axis`/`.active-vertex` (:20032-20036) и `.vertex` (:20038); - вызов `_renderMarkupLayer` (:17553) стоит **до** `_renderWallBodies` (:17565), которая, в свою очередь, стоит **до** `_renderPlanSnapOverlay` (:17582) — ровно та последовательность, что и в ТЗ; - `_renderPlanSnapOverlay` (:19843) и геометрия `buildPlanSnapGeometry` (`src/plan-snap-overlay.ts:253`) действительно пропускают активный драфт (`if (draft.id === options.activeDraftId) continue`) — второй заявленный механизм подтверждён, не выдумка; - `physical-geometry.ts:210-221` подтверждает, что каждый персистнутый сегмент `room_drafts` немедленно попадает в непрозрачные тела (`draftSegments`/`drafts`) без исключения активного драфта — то самое тело, которое перекрывает ось; - «hit-слой», упомянутый в контракте как остающийся на месте элемент `_renderMarkupLayer`, — реален: `_renderPhysicalEditorLayer()` (:19711, вызывается на :20008) рисует `.physical-hit` (прозрачные линии/пути для перетаскивания драфтов/перегородок/колонн, `pointer-events: none` вне инструмента «Выбор», `src/styles.ts:2256-2264`) — контракт не подменяет несуществующим термином существующий механизм; - цвет `#ffc14d` из AC1 подтверждён как реальный `stroke`/`fill` `.pathline` и `.vertex` (`src/styles.ts:1803-1857`) — пиксельная проба технически осмысленна и отличима от штриховки масонри; - AC3 ссылается на существующий `demo/smoke_plan_snap_overlay.mjs` — файл существует. 4. `docs/specs/228-plan-drawing-problems.md` — беглая проверка, что `active-axis`/`active-vertex` действительно история #228 (проекция оси активного отрезка), заявленная в issue как смежная, не пересекающаяся работа — согласуется, конфликта скоупа нет. 5. Сверил критерии лёгкого трека (§5 PROCESS.md) построчно: сложность/риск ≤3 (аналитика — 2/10), одна поверхность, без миграции конфига, поведение уже описано в USER-GUIDE (не новый UX-контракт), перф/touch не задеты (перестановка существующих DOM-узлов, ни одного нового элемента). Шаблон ТЗ соответствует §5 буквально: проблема · контракт · AC · откат, файла в `docs/specs/` нет — верно для `small`. 6. Комментарии issue — один, аналитика владельца (S2→S4, метка `small` поставлена и объяснена). Прежнего вердикта ревью ТЗ нет — это первый заход, раздел «Унаследовано из r0» не применяется. ## Находки Блокирующих (High/Medium) не найдено. Один пограничный момент, не тянущий на находку, а не пропущенный молча: контракт не называет позицию нового слоя относительно `_renderResizeMeasurements`/`_renderRoomHoverOutline`/диагностического оверлея скрытых стен/превью размещения проёма — всех элементов, которые сейчас лежат между `_renderWallBodies` и `_renderPlanSnapOverlay`. Это не продуктовый вопрос: перечисленные оверлеи активны для других инструментов (resize/двери-окна) и не пересекаются по времени с активной цепочкой «Стены», кроме, возможно, подсветки наведённой комнаты (`roomHover`), которая может сработать параллельно с рисованием. AC2 не требует конкретной позиции внутри этого промежутка — только «тела → слой цепочки → снап-оверлей», и это достаточно проверяемо смоком. Оставляю как техническую деталь на усмотрение реализации, как и позволяет §7.1 («всё, чего пользователь не наблюдает, агенты решают сами»); не блокирует и не снижает вердикт. ## Что проверено и корректно - Оба продуктовых вопроса (что видит пользователь, какой объём входит в задачу) не требуют обращения к владельцу: ожидаемое поведение уже зафиксировано в USER-GUIDE.ru.md, объём — один модуль, одна визуальная причина с двумя техническими механизмами, обе описаны и обе имеют решение. Владельцу вопросов не задавалось — и это оправдано, а не пропущено. - Причина дефекта — не догадка, выданная за факт: каждое утверждение о коде (номера строк, имена классов/функций, порядок вызовов, поведение `buildPlanSnapGeometry`) подтверждено чтением текущего `dev` (см. выше). - Контракт однозначен: что именно переносится (`.pathline`, `.vertex`, `.active-axis`, `.active-vertex`), откуда, куда (между конкретными двумя существующими вызовами), что не меняется (геометрия/резолвер снапа, жёлтая стилистика, всё остальное содержимое markup-слоя). - AC1-AC3 — каждый проверяем и указывает способ доказательства (`smoke` новый/существующий, ревью кода). AC1 задаёт конкретные пиксельные точки (середина оси первого сегмента, центр промежуточного узла) и конкретный ожидаемый цвет, отличимый от штриховки — тест способен упасть на текущем `dev`, что и заявлено явно («падает на текущем dev»). AC3 явно фиксирует границу: `plan-snap-overlay.ts` не входит в дифф. - Откат тривиален и назван верно: одиночный revert, ни новых полей, ни миграций, ни persisted-состояний — соответствует характеру чисто композиционного изменения. - Критерии лёгкого трека выполнены все одновременно, метка `small` не оспаривается. - Не найдено расхождений ТЗ с `docs/SCOPE.md`, `docs/USER-GUIDE.ru.md`, `docs/UX-MODES.md`: изменение целиком внутри `hp-editor-only-layer`, гейтится `this._markup`, View не задет, инвариант блокировок (SCOPE.md, «The lock invariant») не касается геометрии стен. ## Чего не проверял - Не запускал код и не собирал бандл — на этапе ревью ТЗ кода ещё нет, проверка была по чтению текущего `dev` (`git log` показывает `dev` на `489f68ee`, что совпадает с SHA, названным в аналитике owner). `typecheck`/`test`/`build` к этому этапу не относятся (это гейты код-ревью, §2.7). - Не проверял, как новый слой будет вести себя при одновременной подсветке наведённой комнаты (`roomHover`) во время рисования цепочки — отмечено выше как техническая деталь реализации, не блокирующая ТЗ. - Не оценивал производительность численно — оценка «перестановка тех же DOM-узлов» правдоподобна на глаз и совпадает с заявлением owner в аналитике, но замер не проводился (задача не требует его: §2.5 требует «влияние на производительность названо», а не измерено, когда оно очевидно нулевое).