Files
houseplan-card/docs/reviews/SPEC-REVIEW-307-r1.md
T
2026-08-25 16:40:13 +00:00

140 lines
13 KiB
Markdown

# 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 требует
«влияние на производительность названо», а не измерено, когда оно
очевидно нулевое).