mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-03 05:08:53 +00:00
148 lines
15 KiB
Markdown
148 lines
15 KiB
Markdown
# 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, снята с запиской в этом
|
||
документе, отдельного цикла не требует.
|