Files
houseplan-card/docs/reviews/SPEC-REVIEW-242-r1.md
T
2026-08-22 17:15:50 +00:00

186 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-242-r1
- Issue: [#242](https://github.com/Matysh/houseplan-card/issues/242) — «Проём в толстой стене рисуется у произвольной грани, а не по центру толщины»
- Этап: ТЗ на ревью (PROCESS.md §2.4)
- Артефакт ТЗ: `docs/specs/242-opening-symbol-center.md`, ревизия `0033138`
- Ветка: `issue/242-opening-symbol-center`
- Заход: **r1**, блокирующих циклов израсходовано **0 из 4** (лимит обычного трека — 4, §4)
- Вердикт: **зелёный**
## Скоуп ревью
Первый заход — полный разбор ТЗ, без ограничения по дельте (§2.10 применяется
только со второго захода).
Проверялось:
1. Соответствие обязательным разделам §7.1 PROCESS.md.
2. Продуктовая рамка — принадлежность задачи job'ам из `docs/SCOPE.md`.
3. Верность диагноза — сверка цитируемого кода с текущим `HEAD` (та же
ревизия, на которой ТЗ написано).
4. Проверяемость и однозначность AC1…AC8.
5. Отсутствие догадок, выданных за факт (решения без пометки, что это
предположение автора, а не решение владельца).
6. Совпадение зафиксированных в ТЗ product-решений (§4) с ответами владельца в
комментариях issue (Q1–Q4).
7. Техническая осуществимость геометрического контракта (§7 ТЗ) — не
декларация ли это того, что текущая модель данных не может обеспечить.
8. Термины интерфейса — сверка с `docs/USER-GUIDE.ru.md`, а не изобретённые.
9. Полнота списка канонических документов, которые придётся обновить.
## Как проверялось
- Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md`, тело issue #242 и все
5 комментариев (аналитика → занятие → вопросы владельцу → решения владельца
→ готовое ТЗ).
- Прочитан `docs/specs/242-opening-symbol-center.md` целиком.
- Прочитаны канонические `docs/WALL-THICKNESS.md` и `docs/ISOMETRIC.md`.
- В `docs/USER-GUIDE.ru.md` найден и прочитан раздел «Толстые стены»
(строки 606–616) и таблица «Настройки проёма» (568–575) — источник
интерфейсной терминологии, которую ТЗ обязано использовать без изменений.
- Прочитан код, на который ссылается диагноз ТЗ, чтобы отделить проверенный
факт от догадки:
- `src/wall-thickness.ts` — `OpeningWallSide`, `resolveOpeningWallAssociation`,
`openingInnerFaceOffsetFromIndex` (строки ~2340–2530);
- `src/partition-openings.ts` — `partitionOpeningFace` (153–165);
- `src/render/opening-symbol.ts` — `openingVisibleMetrics`,
`renderOpeningVisibleGeometry` (1–147);
- `src/houseplan-card.ts` — рендер `op-hit`/`op-outline` (18530–18588) и
позиционирование lock-бейджа (18590–18618).
- Продуктовый код и ТЗ не редактировались — ревьюер только читает.
Гейты для этапа ТЗ не запускались: PROCESS.md §8 требует их на код-ревью, а
здесь нет ни строчки кода. Автор уже отчитался о `node scripts/check-docs.mjs`
для diff'а этого захода (только `docs/specs/**`), что достаточно для этой
стадии.
## Находки
Нет. Ни одной High, Medium или Low.
## Что проверено и признано корректным
**Структура ТЗ (§7.1).** Все обязательные разделы на месте: сценарий и
персона (§1), что человек увидит до/после (§2), диагноз/проблема (§3),
скоуп и не-скоуп (§6), контракт поведения (§7–8), данные/миграция/i18n/a11y/
touch (§9), перф и безопасность (§10), AC1…AC8 с доказательством (§11), план
автотестов (§12), mutation guards (§13), гейты (§14), release-артефакты
(§15), откат (§16), риски (§17), явный блок принятых технических
предположений (§18). Ничего не подменяет продуктовый вопрос техническим и
наоборот.
**Продуктовая рамка.** Задача закрывает J4 («от нуля до плана без внешнего
редактора» — точность геометрии) и J6 («план остаётся достоверным при
эволюции комнат» — независимость от порядка `space.rooms`); во View
дополнительно поддерживает J1 (одинаковое чтение геометрии независимо от
внутреннего порядка данных), как и написано в аналитике issue. Ни одна
строка `docs/SCOPE.md` не нарушается, новых поверхностей продукта не
добавляется.
**Диагноз подтверждён чтением, а не поверил автору на слово.** Прочитанный
код на актуальном `HEAD` (`0033138`, тот же коммит, на котором писалось ТЗ)
подтверждает каждое утверждение §3 ТЗ:
- `openingInnerFaceOffsetFromIndex` действительно сортирует кандидатов по
`order` — полю, комментарий к которому в коде прямо называет его
«first matching room edge in config order» (wall-thickness.ts:2355) — и
берёт `available[0]` как «natural» грань (2513–2520);
- `partitionOpeningFace` действительно фиксирует знак `side` от `flipV` и
умножает его на `resolved.axis` — направление, которое зависит от того, как
сохранены `partition.a`/`partition.b` (partition-openings.ts:157–164);
- `renderOpeningVisibleGeometry` переносит только группу `body` (дуга/створки/
стекло) через `translate(swingTx swingTy)` (opening-symbol.ts:74–137);
косяки (`op-jamb`-линии на `±half` — здесь и далее «half» это `jambHalf`,
то есть половина физической толщины) рисуются вне этой группы (140–146) —
то есть **уже сейчас** структурно независимы от face-сдвига. Это прямо
подтверждает пункт контракта ТЗ §7 «Косяки всегда остаются на `±cm/2` … и
не входят в translated группу» — не предположение, а описание уже
существующей структуры кода, которую нужно не разрушить, а сохранить.
**Геометрический контракт (§7 ТЗ) реализуем на текущей модели данных**, а не
декларация невозможного:
- Для стены комнаты `side` (`-1`/`1`) уже вычисляется из `opening.angle` через
`nx = -sin(rad), ny = cos(rad)` и знака `inward`-нормали ребра
(wall-thickness.ts:2419–2441) — это уже канонический локальный базис самого
проёма, **не зависящий от `order`**. Order используется только для выбора,
какая из двух сторон общей стены становится «natural» (unflipped) —
единственное, что требует замены на фиксированное правило по локальной оси.
Никакой новой геометрической информации придумывать не нужно.
- Для partition та же независимая от направления рисования величина уже
доступна — тот же `opening.angle`, что и для room-wall (сейчас не
используется в `partitionOpeningFace`, но используется параллельно для
room-wall face). Перевести partition-резолвер на тот же базис, вместо
`resolved.axis`, зависящего от `a`/`b`, технически прямолинейно.
- **Lock-бейдж переживёт правку без отдельного контракта**, хотя ТЗ (§18.3)
формулирует это как «принятое предположение»: `_renderOpeningLocks`
(houseplan-card.ts:18605–18616) вычисляет офсет бейджа как
`lockOffset * sign`, где `lockOffset` — фиксированная величина в grid units,
**не связанная с `face.ox/oy`/`cm`** вообще; она использует только знак
(`gateFace.side` либо `flip_v ? -1 : 1`). Смена перевода видимой группы с
«сдвиг на грань» на «центр» этот знак не меняет ни для одного из
зафиксированных в контракте случаев (§7 ТЗ) — прочитано и проверено
чтением, вывод сильнее, чем у самого автора: это не предположение,
которое ревьюер не оспорил, а проверенный факт.
**Верность отражения решений владельца.** Раздел §4 ТЗ построчно совпадает с
ответами владельца из комментария `2026-08-22T17:07:08Z` на все четыре вопроса
(Q1 flip_v-совместимость, Q2 направление ворот, Q3 parity поверхностей, Q4
окно целиком) — без искажений и без добавления нового необсуждённого
поведения. Технические детали (§18: формула локальной оси, имя типа
visual-offset) явно помечены как «принято предположительно, менять свободно»
и не выданы за продуктовое решение.
**Проверяемость AC.** Все восемь AC (§11) сформулированы через числа и
конкретные наблюдаемые свойства (нулевое смещение, идентичность метрик при
перестановке, сохранение full-depth jambs, отсутствие diff в schema), а не
через «выглядит по центру» — ровно то, что требует сама постановка issue
(«AC стоит формулировать через числа»). У каждого указан способ доказательства
в допустимом словаре (`unit`/`smoke`/`golden`/`review`).
**Терминология.** «Открывается в другую сторону» и «flip_v» использованы как
в `docs/USER-GUIDE.ru.md` (568–575), без изобретения нового языка. ТЗ явно
называет тот самый абзац «Толстые стены» (606–616) устаревшим фактическим
описанием («Створка/дуга двери и окна смещается к внутренней грани комнаты, а
створки ворот — к наружной») и относит его обновление к release-артефактам
своего user-visible коммита (§15) — список канонических документов на
обновление (`USER-GUIDE.ru.md`, `WALL-THICKNESS.md`, `ARCHITECTURE.md`,
`ISOMETRIC.md`, changelog RU+EN) полон, ничего из задетых поверхностей не
забыто.
**Не-скоуп обоснован.** Passage (нет видимого символа), wall cut/jamb depth/
opening length, lock badge, hitbox, Glow/sun/decor/vacuum, новый Iso UX или
config-migration — всё это корректно исключено и не пересекается с
задекларированным контрактом §7–8.
## Чего не проверял
- **Не запускал автотесты/гейты** — на этапе ТЗ кода нет, гейты §8
относятся к код-ревью.
- **Не проверял `docs/ARCHITECTURE.md` целиком** — только убедился, что файл
фигурирует как канонический для этого изменения и включён в release-
артефакты §15; содержимое самого файла не читал, это не требуется для
вывода о полноте ТЗ.
- **Не оценивал `docs/specs/README.md` рассинхрон колонки «Статус ТЗ»**
(известный открытый пункт PROCESS.md §7.3.1, не относится к #242) — запись
строки #242 в таблице P2 корректна и не мой предмет.
- **Не проверял `iso-openings.ts`/`buildIsoOpeningBasis()` построчно** —
прочитал только его роль по `docs/ISOMETRIC.md` («door has one jamb-hinged
leaf… mirrors the existing opening-symbol transform algebra»); ТЗ верно
определяет его как одну из четырёх поверхностей parity (AC5), детальную
реализуемость там не проверял глубже необходимого для вывода «этот
контракт возможен» — она подтверждена уже на room-wall/partition пути, а
Iso по документации использует ту же алгебру.
- **Не проверял mutation-gate script и наличие тестовых фикстур** — на этапе
ТЗ они не существуют, это работа код-ревью.
- Никаких browser-smoke/golden не запускал — на этапе ТЗ нет рендера для
сравнения.
## Заключение
ТЗ полно по составу разделов, продуктово обосновано, верно отражает решения
владельца без домыслов, а диагноз и предложенный контракт подтверждены
чтением актуального кода (в том числе в пунктах, которые сам автор пометил
как непроверенное предположение — lock-бейдж, независимость косяков). High/
Medium-находок нет. Задача может двигаться в `S5-ready`.
**Вердикт: зелёный · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 0 → в задаче**