Files
houseplan-card/docs/reviews/SPEC-REVIEW-313-r1.md
T
2026-08-26 05:06:03 +00:00

15 KiB

SPEC-REVIEW-313-r1

Issue: #313 · этап: ТЗ (spec) · трек: small (лёгкий) · заход: r1 · лимит циклов ТЗ: 2 · израсходовано: 0

Скоуп

Инструмент «Толщина» (_wallThickHit/_wallThickHover/_wallThickClick/_wallThickApply, src/houseplan-card.ts) сейчас видит только атомарные интервалы контуров комнат (wallIntervals(space.rooms, …)) и не видит независимую кладку — перегородки (space.partitions) и сегменты сохранённых черновиков (space.room_drafts[].points). ТЗ расширяет резолвер, hover, диалог и запись на все три источника, с приоритетом независимой кладки при наложении и без кнопки «на всю комнату» для неё. Модуль один, файл один — критерии small (сложность/риск ≤3, одна поверхность, без миграции, без нового UX-контракта, без влияния на перф/touch) соответствуют содержанию задачи.

Первый вопрос по docs/SCOPE.md: чью работу закрывает правка. Инструмент правки геометрии Plan-редактора — часть J6 («Keep the plan true as the home evolves», два редактора, drag/resize/merge/split). Конфликта со SCOPE нет: это устранение дыры в уже существующем, одобренном инструменте, а не новая функциональность.

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

Ревью ТЗ этапа S4-spec-review, r1 — полный разбор (первый заход, дельты нет). Читал: docs/SCOPE.md, AGENTS.md, PROCESS.md (§1–§12), тело issue #313 и единственный комментарий (аналитика + решения владельца от 2026-08-26), канонический документ подсистемы docs/WALL-THICKNESS.md, срез docs/USER-GUIDE.ru.md по инструменту «Толщина», связанные issues #308 (закрыт), #306 (open, S3-spec), #229 (закрыт), #303 (закрыт).

Код читался в дереве dev @ a78deb7c (текущий HEAD), не исполнялся — это ревью ТЗ, не код-ревью, автотестов ещё нет. Каждое фактическое утверждение ТЗ (сигнатуры, существование функций/полей, номера строк, тексты тостов, содержимое смоков) проверено чтением исходников и docs/WALL-THICKNESS.md, а не принято на слово:

  • _wallThickHit (:11915) действительно перебирает только wallIntervals(space.rooms, …) — подтверждено чтением, совпадает с описанием «Причина (код)».
  • _wallThickApply (:11978) действительно пишет только через sp.walls / setWallThicknessForRoom — writer для partition.cm/draft.segments[i].cm отсутствует, подтверждено.
  • wallThickHoverHalfUnits(hit.cm, …) уже принимает cm (:11946, импорт :312) — подтверждено, hover-контракт из #303 переиспользуется без изменений.
  • PartitionCfg (src/types.ts:64) и RoomDraftCfg.segments[i] (src/types.ts:56-61) уже содержат обязательное числовое поле cm — модель не меняется, миграция не нужна.
  • docs/WALL-THICKNESS.md §9 УЖЕ фиксирует инвариант «1–100 см для перегородок и сегментов драфта, ноль не существует» и «Invalid input blocks the commit and reports the valid range» — решение владельца №2 (запретить 0/пусто для независимой кладки) не догадка, а совпадение с задокументированным контрактом модели. Это же объясняет, почему у комнатных стен 0 легитимен (§6 канона: «empty/0 clears»), а у независимой кладки — нет: два разных, уже описанных контракта, ТЗ их не путает.
  • toast.wallthick_pick / toast.wallthick_open / toast.physical_range (src/i18n/ru.json:533-537) существуют и достаточно общие для переиспользования на перегородках/драфтах — новых ключей i18n действительно не требуется, как заявлено в аналитике.
  • _commitPhysicalGeometry + history.wall_thickness (:12017) — существующая физическая транзакция с одним Undo, переиспользуется корректно.
  • Правило приоритета независимой кладки над контуром комнаты уже реализовано и используется в смежном коде — _boundaryBlocked (:11285-11309, комментарий «Independent masonry owns its hit zone…») перебирает wall_columns, partitions и room_drafts именно в этом порядке приоритета для операций границы/select. Формулировка ТЗ «согласуется с select-инструментом» подтверждена, а не голословна.
  • _activeDraftId (:1548) переживает переключение инструмента/пространства (:1291, :2188) — «активный драфт инструменту не виден» описывает реальную достижимую ситуацию (недорисованная цепочка, отложенная переключением тула), а не искусственный кейс; исключение активного драфта уже так работает у snap-геометрии (excludeDraftId, :6746; комментарий :20349) — переиспользование обосновано.
  • Три смока, названные в AC4, существуют: demo/smoke_wall_thickness.mjs, demo/smoke_wallthick_hover_width.mjs, demo/smoke_resize_wall_thickness.mjs.
  • Кнопка «на всю комнату» (wallthick.apply_room, :12558-12560) сейчас рендерится безусловно — условный рендер по «есть ли roomId/kind room» технически тривиален, контракт (п.3) реализуем как описано.
  • #308 (пример наложения перегородки на ребро комнаты, фикстура-экспорт в приложении к отчёту) — issue закрыт без комментария к закрытию и без метки rejected, при этом сохраняет статусную метку S1-new (в нарушение §2.9/§9 PROCESS.md — закрытый issue не должен нести S*). Это не блокирует #313: своя AC3 (приоритет при наложении) самодостаточна и не зависит от того, будет ли когда-нибудь сделана «согласованная правка обоих источников» из решения владельца №3. Но пункт «остаётся скоупом #308» из решения владельца сейчас ссылается в никуда — фиксирую это как наблюдение, не как находку против #313.

Гейты кода не гонялись — на этапе ТЗ кода ещё нет, тестировать нечего (typecheck/test/build/check-docs относятся к код-ревью, §2.7/§8).

Находки

Ни одной High/Medium. Одна Low, снимаю с записью (не блокирует, автору можно поправить попутно при написании ТЗ AC/PR, отдельного цикла не требует):

  • Low — неверный номер строки в цитате. В разделе «Что нужно» (п.1) и в разделе «Причина» issue правило «Independent masonry owns its hit zone» указано по адресу :11104. На dev @ a78deb7c по этому адресу лежит фрагмент диалога decor-текста (цветовой пикер), правило по факту находится в _boundaryBlocked, src/houseplan-card.ts:11285. Раздел «Контракт» (та часть ТЗ, что реально нормативна) этот номер не повторяет — там же цитата дана без строки, поэтому имплементация не пострадает. Правлю с записью: верный адрес — src/houseplan-card.ts:11285.

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

  • Трек small выбран обоснованно, все пять критериев §5 выполнены (одна поверхность/один файл, риск/сложность ≤3, без миграции, без нового UX-контракта — кнопка apply_room просто скрывается там, где нет комнаты, остальной диалог тот же, — без влияния на touch/perf).
  • Обязательные для light-трека разделы (проблема · контракт · AC · откат) присутствуют, ни один не пуст.
  • Три продуктовых решения владельца (комментарий 2026-08-26) корректно перенесены в «Контракт» и не переизобретены заново.
  • Все 4 AC пронумерованы, каждый называет способ доказательства (smoke/unit), AC1/AC2 явно фиксируют текущее красное состояние («падает на текущем dev — hit = null»), AC3 задаёт конкретную регрессионную фикстуру (#308), AC4 защищает от регрессии по существующим гейтам — тест на «AC умеет упасть» проходит на уровне формулировки (для AC1/AC2 это прямо написано).
  • Ни одного продуктового вопроса не осталось открытым: три развилки, требовавшие решения владельца (кнопка диалога, ноль/пусто, приоритет при наложении), закрыты явными решениями в комментарии аналитики, а не угаданы автором.
  • Утверждений о поведении, не подтверждённых документом/кодом и не помеченных как предположение, не найдено — единственная фактическая неточность (строка :11104) разобрана выше как Low.
  • Откат («один revert, без миграций и новых полей конфига») соответствует факту: модель данных не меняется, меняется только резолвер/writer инструмента.
  • Конфликта со docs/SCOPE.md и docs/TOUCH-SUPPORT.md не нашёл: правка редактора Plan (десктоп-первый инструмент), в View не протекает.

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

  • Не проверял по коду, что hit-резолвер после правки не даст обратной регрессии для контуров комнат при наличии рядом коллинеарной перегородки НЕ на кейсе #308 (общий случай «рядом, но не точно на ребре») — в ТЗ такой AC не заявлен явно за пределами AC3/AC4, это забота код-ревью и, при необходимости, находка для отдельного цикла на этапе кода, а не пробел ТЗ (AC4 достаточно узко и корректно защищает именно регрессию контуров).
  • Не проверял столбы (space.wall_columns) на предмет того, должен ли инструмент «Толщина» также брать их — симптом, аналитика и AC ни разу их не упоминают, а в docs/USER-GUIDE.ru.md у колонны уже отдельный диалог размера через select (не через «Толщина»); границу считаю намеренной и не отношу к находкам.
  • Не прогонял никаких автотестов/смоков — на этапе ТЗ кода нет, гонять нечего; сами тексты AC1/AC2 содержат обязательство, что новый смок «падает на текущем dev», это будет предметом код-ревью, а не этого документа.
  • Не проверял #306 (замена виртуальных стен стенами cm=0) по существу — он открыт, в S3-spec, blocked, и в контракт #313 входит только требованием «точка записи не должна требовать переписывания под будущую ветку 0», что является технической, а не продуктовой договорённостью и не подлежит проверке на этом этапе.

Вердикт

Готово к разработке. Both AC-доказательства и продуктовые развилки закрыты решениями владельца; единственная находка — Low, снята с запиской в этом документе, отдельного цикла не требует.