16 KiB
SPEC-REVIEW-242-r1
- Issue: #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 применяется только со второго захода).
Проверялось:
- Соответствие обязательным разделам §7.1 PROCESS.md.
- Продуктовая рамка — принадлежность задачи job'ам из
docs/SCOPE.md. - Верность диагноза — сверка цитируемого кода с текущим
HEAD(та же ревизия, на которой ТЗ написано). - Проверяемость и однозначность AC1…AC8.
- Отсутствие догадок, выданных за факт (решения без пометки, что это предположение автора, а не решение владельца).
- Совпадение зафиксированных в ТЗ product-решений (§4) с ответами владельца в комментариях issue (Q1–Q4).
- Техническая осуществимость геометрического контракта (§7 ТЗ) — не декларация ли это того, что текущая модель данных не может обеспечить.
- Термины интерфейса — сверка с
docs/USER-GUIDE.ru.md, а не изобретённые. - Полнота списка канонических документов, которые придётся обновить.
Как проверялось
- Прочитаны
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 → в задаче