Files
houseplan-card/docs/reviews/SPEC-REVIEW-304-r1.md
T
2026-08-25 13:06:34 +00:00

12 KiB
Raw Blame History

SPEC-REVIEW-304-r1

Issue: #304 «Редактор плана: режим «Толщина» скрывает часть осевых линий и узлов стен» Этап: spec (лёгкий трек, метка small) Заход: r1 · блокирующих циклов израсходовано 0 из 2 (лимит лёгкого трека — 2) Ревьюер: Claude (сессия ревью ТЗ), артефакт — комментарий в issue + этот документ (по правилу §5 документ не обязателен, но публикуется для трассируемости).

Скоуп

ТЗ живёт в теле issue #304 (комментарий владельца от 2026-08-25 подтверждает small). Задача: unify статический слой архитектурных осей/узлов Plan editor (plan-snap-overlay) так, чтобы он не зависел от активного MarkupTool — сейчас полный слой рендерится только для draw, а в остальных девяти инструментах его подменяет hidden-wall-diagnostic-overlay, который по контракту показывает только скрытые перекрывающиеся независимые источники (draft/partition), а не полную геометрию комнат. AC1–AC7, план проверок, риски, откат и release-артефакты присутствуют.

Продуктовая рамка: J6 SCOPE.md — «Keep the plan true as the home evolves», конкретно «достоверная топология при поддержании плана». Owner-комментарий явно ссылается на J6. Соответствие подтверждаю: задача не расширяет и не меняет ни один Core user job, чинит расхождение представления внутри уже принятого поведения инструмента «Стены».

Как проверялось

Ревью ТЗ выполнялось не на веру автору, а с чтением текущего кода — задача описывает конкретный технический механизм бага, и его нужно было заземлить, а не принять как заявление:

  1. docs/SCOPE.md, AGENTS.md, PROCESS.md §2.4/§2.10/§5/§7.1/§7.2/§12 — рамка процесса и лёгкого трека.
  2. Тело issue #304 и оба комментария владельца (аналитика + передача на ревью).
  3. docs/USER-GUIDE.ru.md:373-379 — подтверждает, что текущая документация уже фиксирует осевые линии как поведение именно инструмента «Стены»; после фикса это предложение придётся расширить на все инструменты Plan editor, что ТЗ и заявляет в Release-артефактах.
  4. src/houseplan-card.ts:
    • 588 — type MarkupTool = 'select' | 'draw' | 'column' | 'merge' | 'split' | 'resize' | 'opening' | 'boundary' | 'wallthick' | 'delroom' — ровно десять значений, как в AC2, не выдумка;
    • 1524-1526 — get _markup(): boolean { return this._mode === 'plan'; } — статический слой физически не может просочиться в View/Device/Backdrop-editor, потому что все точки рендера overlay обёрнуты в this._markup ? … : nothing; AC5 — не гипотеза, а прямое следствие текущей структуры кода;
    • 17565 _renderWallBodies, 17572-17573 _renderHiddenWallDiagnosticOverlay (без условия на _tool, то есть рендерится во всех markup-инструментах), 17578-17579 ${this._markup && this._tool === 'draw' ? … this._renderPlanSnapOverlay() … : nothing} — это буквальное подтверждение корневой причины из issue: полный plan-snap-overlay гейтится на draw, diagnostic-слой остаётся один во всех остальных.
  5. src/plan-snap-overlay.ts:
    • 166-215 buildHiddenWallDiagnosticGeometry — hidden = sources.filter(source => source.kind !== 'room' && sources.some(other => other !== source && positiveCollinearOverlap(...))) — буквально подтверждает контракт «показывает только скрытые перекрывающиеся независимые источники», room-only рёбра без партнёра по перекрытию никогда сюда не попадают. Это и есть причина «части осей нет в Толщина» — не домысел автора ТЗ, а прочитанный код;
    • 228 buildPlanSnapGeometry — существующая каноническая геометрия, которую фикс предлагает переиспользовать для статического слоя во всех инструментах (contract p.3);
  6. demo/smoke_plan_snap_overlay.mjs:236-239 — result.otherPlanToolsHaveHiddenDiagnostic = !overlay() && !!diagnostic && … — буквально закрепляет !overlay() (полного слоя нет) для инструмента select как ожидаемое поведение. Это подтверждает заявление issue «smoke сейчас закрепляет это расхождение вместо проверки межинструментального инварианта» — не голословно, а строкой теста.
  7. demo/golden/matrix.mjs, demo/golden/harness.mjs — существуют, ссылка в скоупе ТЗ не на вымышленную инфраструктуру.
  8. docs/TOUCH-SUPPORT.md — grep по overlay/snap не дал контрактных ограничений на этот слой; заявление «touch-контракт не расширяется» не противоречит канону (слой был и остаётся pointer-events: none, десктоп-first редактор не меняется).
  9. docs/WALL-THICKNESS.md, docs/CANVAS.md, docs/UX-MODES.md — grep по overlay/wallthick не нашёл предписаний, которым ТЗ противоречило бы.

Находки

Нет находок уровня High или Medium. Задача полностью укладывается в лёгкий трек: сложность/риск, заявленные владельцем (3/10 и 3/10), подтверждаются техническим разбором — правка изолирована в рендер-гейтинге одного файла плюс возможное разделение геометрии в plan-snap-overlay.ts, без миграции, новых i18n-ключей, нового UX-контракта (видимое поведение просто перестаёт зависеть от инструмента — это и есть заявленный "после") и без touch-влияния.

Low, не блокирует, не требует правки:

  • AC1 фиксирует магическое число «шесть узлов» для контрольного участка. Это не домысел: число заземлено в приложенных к issue скриншотах (2026-08-25_15-46-52.png — полный вид «Стены») и будет закреплено unit-фикстурой на этапе реализации (план проверок, п.1). Претензий к формулировке нет, отмечаю для полноты «чего не проверял».

Что проверено и корректно

  • Обязательные разделы §7.1 (для лёгкого трека — §5: проблема, контракт, AC1…ACn с доказательством, откат) присутствуют, ТЗ фактически даёт больше, чем требует лёгкий трек (сценарий, эдж-кейсы, риски, release-артефакты).
  • Каждый AC (1–7) однозначен, имеет способ доказательства (unit/smoke/golden/ «ревью кода») и указывает конкретный наблюдаемый инвариант, а не реализацию.
  • Корневая причина бага, названная в issue, дословно подтверждается кодом — это не «догадка, выданная за решение»: технический механизм проверен построчно (см. выше).
  • Контракт поведения (пп.1–7) полон: перечисляет все 10 инструментов, явно разделяет статическую топологию и transient-состояния инструмента «Стены», фиксирует presentation-only и pointer-events: none, границы режимов (View/Device/Backdrop) и отсутствие промежуточного пустого кадра при переключении.
  • Скоуп/не-скоуп корректно исключают алгоритмы привязки/записи толщины и построения стен — фикс ограничен представлением, не моделью.
  • Совместимость, миграция, i18n, touch, performance разобраны и обоснованы отсутствием влияния, а не пропущены молчанием.
  • Риски (двойная отрисовка, порядок рендера, случайная утечка transient-маркеров, инвалидация кэша на pointermove) названы предметно и совпадают с зонами, которые я сам выявил бы при чтении кода рендер-конвейера — заявка не занижает риск.
  • Откат тривиален и реалистичен (revert рендер-гейтинга, схема/данные не меняются).
  • Owner-аналитика подтверждает соответствие job J6, дубликаты проверены и отклонены предметно (#232 — про другое: hover при рисовании).

Чего не проверял

  • Не проверял на глаз итоговое число узлов/сегментов для контрольной fixture построчным выполнением buildPlanSnapGeometry/buildHiddenWallDiagnosticGeometry — это задача unit-теста на этапе реализации, а не спецификации; на этапе ТЗ важно было убедиться, что функции и семантика существуют и соответствуют описанному контракту, что сделано.
  • Не запускал npm test/npm run build/typecheck — на этапе ревью ТЗ гейты кода не прогоняются (нечего собирать, код не менялся), это относится к этапу код-ревью (§2.7).
  • Не проверял golden/смоук-набор целиком — задача их ещё не создала (в скоупе только план их появления), это тоже предмет код-ревью.
  • Не оценивал реальный экспорт houseplan-space-convergence-test-...json, приложенный к issue (файл не запрашивался и не был нужен: код уже подтверждает механизм независимо от конкретного экспорта).

Вердикт

Зелёный. ТЗ проверяемо, однозначно, технически заземлено чтением кода, укладывается в лёгкий трек и закрывает продуктовую строку J6. High/Medium-находок нет.