24 KiB
SPEC-REVIEW-141-r1
- Issue: https://github.com/Matysh/houseplan-card/issues/141
- ТЗ под ревью:
docs/specs/141-wall-junctions.md(коммит59c66dd, веткаissue/141-wall-joints) - Роль: ревьюер ТЗ (не автор), этап
S4-spec-review - Трек: обычный (не
small/trivial) — сложность/риск 7/10, две+ поверхности (перегородки/drafts и рисование комнатных стен), влияние на golden, свет и производительность; лёгкий/короткий трек владелец и автор корректно не применили. - Цикл: r1/4
Скоуп ревью
Проверялось соответствие ТЗ:
docs/SCOPE.md— попадание в Core user jobs, отсутствие расширения скоупа;PROCESS.md§2.4, §2.5 (DoR), §7.1 (обязательные разделы), §3/§12 (запреты);AGENTS.md— классы файлов, ветка, трейлеры коммита ТЗ;- каноническим документам:
docs/WALL-THICKNESS.md(модель толщины, mitre/bevel, union тел),docs/LIGHT.md(барьеры/_lightBarriers),docs/SUN.md(occluders),docs/ISOMETRIC.md(wallBodiesGeometry+ extra physical bodies),docs/CANVAS.md(координаты/масштаб),docs/TOUCH-SUPPORT.md(best-effort editors),docs/CONFIG-COMPATIBILITY.md(миграция — в задаче её нет, но проверено, что это верно); docs/USER-GUIDE.ru.md— терминология «Контур комнаты» / «Перегородка»;- фактическому коду (
src/wall-thickness.ts,src/physical-geometry.ts,src/houseplan-card.ts) — на предмет того, что технический диагноз ТЗ не является непроверенной догадкой, выданной за факт.
Как проверялось
- Прочитан весь тред issue #141: исходное описание с уточнением владельца
(«проявляется и на прямых, и на непрямых углах»), полная аналитика
(ценность 8/10, сложность/риск 7/10, P2, тип
bug, полный трек) с Q1–Q3 и явными defaults, решение владельца «принимаю все defaults», финальный комментарий автора со ссылкой на коммит ТЗ. - Сверены обязательные разделы ТЗ (§7.1 PROCESS.md) построчно — таблица ниже.
- Прочитан код и подтверждены построчно все технические утверждения §3 ТЗ:
drawWallPreviewD()(src/wall-thickness.ts:490) действительно строитoutset−insetтолько дляclosed-контура (:496); открытый путь — независимый quad на каждый сегмент (:512-529). Диагноз ТЗ п.1 точен.partitionBody()(src/physical-geometry.ts:41) строит прямоугольник с плоскими торцами без топологии узла;physicalBodies()(:89) просто собирает такие прямоугольники в список. Диагноз ТЗ п.2 точен.MITRE_LIMIT = 4(src/wall-thickness.ts:28) и его использование в трёх местах (:669, :1429, :1805) существуют — ссылка ТЗ §7.2.3/AC3 не изобретена.- Изометрия (
src/houseplan-card.ts:4606-4611) уже вызываетwallBodiesGeometry(..., extras)сextras = physicalBodies(...)— значит full/static/iso уже объединяют независимые тела черезwallBodiesUnionPath/wallBodiesGeometry(тот же приём подтверждён вsrc/space-render.ts:338для статической карточки). Ровно то, что ТЗ утверждает в §3.5 и §8.2 («full/static/isometric render уже объединяют…»). _lightBarriers()(src/houseplan-card.ts:13556) вызываетwallBodiesGeometry()безextras(:13623) и затем отдельно кладёт сырые ребра каждогоphysical-тела в occluders (:13637:for (const body of physical) occluders.push(...polygonSegments(body));). Солнце (src/houseplan-card.ts:12705-12716) вызываетdirectionalOccluders(physical, ...)напрямую на тех же сырых прямоугольниках. Это подтверждает диагноз ТЗ §3.5/§8.2: свет и солнце сегодня действительно обходят raw-прямоугольники отдельно от joined-пути рендера, а не голословное утверждение.floorMinusBodies()вызывается с тем же_physicalBodiesR()(сырые тела) и для чистой площади (:7366, :7388), и для проверки source-inside (:11655) — совпадает с AC8.
- Прочитан
docs/WALL-THICKNESS.mdцеликом: подтверждён существующий контракт mitre/bevel для комнатных стен (§3) и отдельный раздел «9. Independent partitions, drafts and columns» — независимые тела уже юнионятся с комнатными только после вырезов проёмов и участвуют в clean-floor/Glow/sun occlusion. ТЗ §7.3.2, §9 и AC6/AC10 корректно продолжают именно эту, а не новую, модель. - Прочитан
docs/LIGHT.mdиdocs/SUN.md: подтверждено, что_lightBarriersдержит один общий барьерный набор на пространство и кэшируется по геометрическому fingerprint, а не по_cfgEpoch— ТЗ §8.3.1-2 корректно продолжает существующий кэш-контракт, не изобретая новый механизм инвалидации. - Прочитан
docs/ISOMETRIC.md:src/iso-walls.tsявно описан как потребитель «каноническогоwallBodiesGeometry()MultiPolygon after openings and extra physical bodies have been resolved» — буквально подтверждает ТЗ §8.2 («full/static/isometric не создают разные join алгоритмы»), т.е. это не домысел автора, а прямая цитата канона. - Прочитан
docs/TOUCH-SUPPORT.md: политика требует, чтобы «новые спецификации editor-фич и код-ревью» явно указывали одно из трёх значенийTouch editor: supported / best effort / not exposed. ТЗ §10 описывает ровно best-effort-поведение словами, но не использует эту точную метку — см. Low-1. - Прочитан
docs/USER-GUIDE.ru.md(раздел «Создание комнаты», таблица «Инструменты плана», раздел «Виртуальные стены»): термины «Контур комнаты» и «Перегородка» в ТЗ (§4.1, §5.3 и др.) совпадают с интерфейсным словарём ровно там, где употребляются полностью; там же нашёл существующую строку «Углы | Соседние тела стен соединяются…» (:347) — актуальное описание, которое release-артефакты ТЗ (§15) обязывают дополнить для partitions — корректно учтено. - Прочитан
src/types.ts:45-72—RoomDraftCfg,PartitionCfg,space.room_drafts,space.partitionsсуществуют как реальные поля схемы, а не придуманы для ТЗ; AC4/AC5 про кросс-типовые соединения (partition↔partition, draft↔partition, partition/draft↔room wall) технически имеют смысл на этой модели. - Проверена запись
docs/specs/README.md:85— строка на #141 добавлена в том же коммите; ссылка issue ↔ ТЗ двусторонняя (ТЗ ссылается на issue в заголовке, issue-комментарий ссылается на файл ТЗ и коммит). - Проверены трейлеры и class-принадлежность:
git diff --stat origin/dev...HEADпоказывает толькоdocs/specs/141-wall-junctions.mdиdocs/specs/README.md(класс C, ни одного файла класса A); коммит59c66ddнесётIssue: #141иUser-Visible: no— корректно для документа ТЗ, который сам не меняет поведение.
Обязательные разделы (§7.1 PROCESS.md)
| Раздел | Есть | Комментарий |
|---|---|---|
| Сценарий (персона/поверхность/момент) | ✅ | §1 — домашний администратор, desktop Plan editor, момент второго клика |
| Что человек увидит до/после | ✅ | §2, «До:»/«После:» — см. Low-2 |
| Проблема (с подтверждённой причиной) | ✅ | §3, шесть пунктов, все проверены по коду (см. выше) |
| Скоуп / не-скоуп | ✅ | §5 / §6, явные границы (без snap #137, без нового cap, без #138) |
| Контракт поведения | ✅ | §7 (геометрия) + §8 (архитектура) |
| Модель данных и миграция | ✅ | §9 — явное «schema не меняется», «читается исправленно, без записи» |
| UX, i18n, accessibility, touch | ✅ | §10 — см. Low-1 |
| AC1…ACn с доказательством | ✅ | §12, 13 штук, каждый с типом доказательства и наблюдаемым результатом |
| План автотестов | ✅ | §13, по подпунктам на unit/smoke/golden/performance |
| Риски | ✅ | §16, таблица с вероятностью/ущербом/снижением |
| Откат | ✅ | §17 |
| Release-артефакты | ✅ | §15, конкретный список файлов документации и обоих changelog |
Все обязательные разделы присутствуют и содержательны. Дополнительно есть раздел «Решения владельца» (§4, явно фиксирует принятые Q1–Q3), архитектурный контракт (§8), план реализации (§14, помечен как техническая свобода) и явный блок «принятые технические предположения» (§18) — соответствует духу PROCESS.md §7.1 об отделении продуктового решения от технического.
Находки
Находок уровня High и Medium нет — новых issue не требуется.
Low-1 — нет явной метки touch-контракта по docs/TOUCH-SUPPORT.md
Файл: docs/specs/141-wall-junctions.md:252-261 (§10)
docs/TOUCH-SUPPORT.md требует: «New editor feature specifications and code
reviews must state one of: Touch editor: supported; Touch editor: best effort / intentionally degraded; Touch editor: not exposed.» §10 ТЗ по
существу описывает best-effort-контракт словами («Plan editor остаётся
desktop-first… новый hover parity не обещается») и корректно ссылается на
safety floor (сохранённая geometry тапнутого сегмента должна совпадать), но не
использует ни одну из трёх канонических формулировок буквально.
Решение ревьюера: Low, не блокирует. При следующей правке ТЗ или в
хендоффе код-ревью достаточно добавить одну строку Touch editor: best effort / intentionally degraded — по содержанию это именно то, что §10 уже
описывает.
Low-2 — «что человек увидит» длиннее одной фразы
Файл: docs/specs/141-wall-junctions.md:28-37 (§2)
PROCESS.md §7.1 требует «одной фразой, без терминов реализации». Раздел
написан двумя короткими абзацами («До:» / «После:»), по одному предложению
каждый; по существу требование выполнено (без терминов реализации, конкретно
и однозначно), но формально это не «одна фраза». Тот же класс находки уже
фиксировался как Low и не блокировал приёмку в SPEC-REVIEW-137-r1.
Решение ревьюера: Low, не блокирует. Косметическая правка на усмотрение автора.
Low-3 — доказательство AC10 («code review») до кода не проверяемо, но это ожидаемо для code-review-этапа
Файл: docs/specs/141-wall-junctions.md:315-321 (AC10, AC11)
AC10 и AC11 указывают доказательство «unit + code review» / «performance +
code review» — это не входит буквально в список §2.5 PROCESS.md
(unit/backend/smoke/golden/«ревью кода»), но «code review» — это то же
самое «ревью кода» иначе сформулированное, и AC11 корректно называет
performance, обязывая код-ревьюера прогнать performance-профиль по правилу
«гейты соразмерны AC» (PROCESS.md §8). Не дефект по существу, тот же класс
находки не блокировал SPEC-REVIEW-137-r1 (Low-3 там).
Решение ревьюера: Low, не блокирует. Оставить как есть.
Что проверено и корректно
- Соответствие
docs/SCOPE.md: задача закрывает J4 («от нуля до плана без внешнего редактора» — администратор должен доверять форме стены в момент рисования, не после замыкания) и J6 («план остаётся правдивым по мере развития» — независимые перегородки не деградируют в кривую геометрию навсегда). Обе строки Closed — это исправление дефекта внутри принятой функциональности, не расширение продукта. - Легитимность полного трека: сложность/риск 7/10, минимум две
поверхности (перегородки и рисование стен комнаты), влияние на golden, свет
и производительность — критерии
small/trivial(§5/§5.1 PROCESS.md) не выполняются ни по одному пункту; полный трек выбран верно. - Продуктовые вопросы закрыты по процессу: Q1 (какие соединения входят),
Q2 (нормализуется ли rubber-band до клика) и Q3 (что делать со свободными
торцами) заданы одним пакетным комментарием, каждый с предлагаемым default,
issue корректно ушёл в
blocked+S3-specдо ответа и вышел изblockedсразу после решения владельца. Ни одна догадка не выдана за факт без пометки — §18 отдельно и явно перечисляет технически свободные решения и прямо фиксирует «нет открытых продуктовых вопросов» (§18.10). - Технический диагноз не голословен. Все шесть пунктов §3 (независимый
путь
drawWallPreviewD, плоские торцыpartitionBody, отсутствие топологии узла вphysicalBodies, отдельный путь mitre для закрытого контура, частичное объединение в full/static/iso против необъединённых barriers у Glow/sun, неподтверждённость отдельного дефекта свободного торца по скриншоту) построчно проверены по исходному коду и совпадают с ним — см. «Как проверялось» п.3. - Не-скоуп (§6) корректно отсекает соседние соблазны: snap-tolerance/#137, автоматическое дробление persisted-геометрии, превращение partition в границу комнаты/HA-зону, новый persisted junction-тип и миграция, новый cap/join selector, #138 (замыкание контура по углам существующей комнаты) — все явно исключены с указанием, почему это не эта задача.
- Регрессионные гарантии сформулированы явно: AC10 поимённо защищает
проёмы комнатных стен, virtual-T, nested/partial стены, экстерьер #123 и
колонны — то есть ровно те механизмы, которые уже используют
MITRE_LIMIT/union и могли бы негласно пострадать от новой топологии узлов. - Производительность: §11/AC11 корректно ссылаются на существующую
60-partition фикстуру
large-house-v1и явно запрещают full-house boolean union на каждый pointermove/HA-tick (§8.3.3) — согласуется сdocs/LIGHT.md(«barriers keyed by geometry fingerprint, never_cfgEpoch») и не вводит новый бюджет без решения процесса. - Модель данных и миграция (§9): корректно заявлено «schema не меняется»,
«читается исправленно без записи», без прямой/обратной миграции — сверено с
docs/CONFIG-COMPATIBILITY.md: задача не создаёт нового compatibility-случая, потому что не меняет persisted-представление. - Release-артефакты (§15) перечисляют конкретные существующие документы
(
WALL-THICKNESS.md,LIGHT.md,SUN.md,ISOMETRIC.md,ARCHITECTURE.md,TESTING.md,STATUS.md,USER-GUIDE.ru.md) и оба changelog в одном implementation-коммите — соответствует §7.1/правилу 11 PROCESS.md. Golden корректно ограничен принятием только черезnpm run golden:accept -- --reviewedпо полному Linux-артефакту (§13.3), perf/golden/browser-suite верно отнесены к пре-релизному, а не implementation-гейту (§8, §11.4 PROCESS.md). - Откат (§17) корректно опирается на отсутствие миграции данных: revert implementation-коммита восстанавливает прежний (дефектный) визуал без риска для сохранённых данных.
- Трассируемость:
docs/specs/README.md:85обновлён тем же коммитом (59c66dd); веткаissue/141-wall-jointsи трейлеры (Issue: #141,User-Visible: no) корректны для документа класса C, который сам не меняет поведение.git diff --stat origin/dev...HEADне содержит ни одного файла класса A — продуктовый код не тронут доS5-ready(правило №1).
Чего не проверял
- Не проверял реализуемость «одного чистого вычисляемого frame» (§8.1) как конкретной структуры данных/API — по правилам ТЗ это явно свободное техническое решение автора кода (§18.2), не предмет ревью ТЗ.
- Не запускал автотесты,
golden,performanceили browser-смоки — на этапеspecэто не требуется; существование фикстур (large-house-v1, реальные поля схемы, конкретные функции и их поведение) проверено чтением кода, а не исполнением. - Не проверял, что boolean-decomposition конкретной реализации (mitre patch
для узла степени 3+, T-разрез сквозного сегмента) технически осуществима
средствами
polyclip-tsв разумное время — это открытое для автора кода техническое решение, отмеченное в ТЗ как свободно изменяемое (§18.3), и фактическая проверка бюджета (AC11) относится к код-ревью и пре-релизному гейту. - Не проверял корректность конкретных числовых оценок аналитики (8/10 · 7/10 · P2) по существу — это поле владельца (PROCESS.md §2.2), уже принято явным решением владельца до написания ТЗ.
- Не проверял связанные issue #123/#137/#138 по существу за пределами того, что понадобилось для верификации ссылок ТЗ (регрессия #123, снэп #137, разграничение с #138) — они не входят в предмет этого ревью.
Вердикт
Зелёный. High: 0, Medium: 0. Три находки Low (отсутствие буквальной метки
touch-контракта по docs/TOUCH-SUPPORT.md; «что человек увидит» длиннее
одной фразы; типы доказательства AC10/AC11 вне буквального перечня §2.5) — ни
одна не блокирует приёмку, все либо правятся косметически при следующей
редакции, либо снимаются этой записью без нового цикла.