Files
houseplan-card/docs/reviews/SPEC-REVIEW-132-r1.md
T
2026-08-18 21:33:56 +00:00

30 KiB
Raw Blame History

SPEC-REVIEW-132-r1

  • Issue: https://github.com/Matysh/houseplan-card/issues/132
  • ТЗ под ревью: docs/specs/132-partition-openings.md (коммит 2aaabc48d65b8886b72578907e9622379807d991, ветка issue/132-partition-openings-v2)
  • Связанный bug в том же scope: #185 (решением владельца исправляется в рамках #132)
  • Роль: ревьюер ТЗ (не автор), этап S4-spec-review
  • Трек: обычный (не small/trivial) — сложность/риск 9/10, несколько поверхностей (placement, geometry, light, room-topology, HA state, i18n, golden); лёгкий трек корректно не применён, файл ТЗ в docs/specs/ создан, как требуется
  • Цикл: r1/4

Скоуп ревью

Проверялось соответствие ТЗ:

  • docs/SCOPE.md — попадание в Core user jobs (J4, с поддержкой J1/J2/J3), отсутствие расширения скоупа, lock-инвариант, правило «никогда не удалять файл по догадке» (задача файлов не касается — у openings нет attachments, §16 ТЗ это явно фиксирует);
  • PROCESS.md §2.4, §2.5 (DoR), §7.1 (обязательные разделы), §12 (запреты);
  • AGENTS.md — классы файлов, ветка issue/132-partition-openings-v2, трейлеры, связь issue ↔ ТЗ в docs/specs/README.md;
  • канонические документы затронутых подсистем: docs/LIGHT.md, docs/WALL-THICKNESS.md, docs/SUN.md, docs/CANVAS.md, docs/UX-MODES.md, docs/CONFIG-COMPATIBILITY.md, docs/TOUCH-SUPPORT.md — на предмет того, что технические утверждения ТЗ о «текущем поведении» не являются непроверенной догадкой, а описывают код и контракты, которые действительно существуют;
  • docs/USER-GUIDE.ru.md — терминология («Стены», «Проём», «Перегородка», «Открытый проём») берётся оттуда, а не изобретается;
  • весь тред issue #132 (7 комментариев, 3 раунда вопросов владельцу, две редакции ТЗ) — на предмет того, что продуктовые вопросы были заданы владельцу, а технические автор решил сам и пометил как предположения.

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

  1. Прочитан весь тред issue #132: исходный отчёт, решение владельца «проём пропускает свет» (2026-08-13), аналитика S2 (сложность 8/10 → 9/10 после переоценки от 2026-08-19 после релиза #173), вопросы Q1–Q3 (комментарий 2026-08-14) и их дефолты, принятые владельцем без изменений (2026-08-15), первая редакция ТЗ (issue/132-partition-openings, коммит da05728), актуализация аналитики после #173 (комментарий 2026-08-19, семь конкретных пунктов о том, что изменилось), вопросы Q4–Q5 и их принятие владельцем вместе с решением включить фикс #185 в этот же issue, финальная редакция ТЗ (issue/132-partition-openings-v2, коммит 2aaabc4).
  2. Прочитаны issue #173 (единый инструмент «Стены», слияние room-outline/partition) и #157 (passage — «Открытый проём») — оба закрыты и уже выпущены в v1.65.0-beta.2 (см. git log — коммит 54c5ca3 test: accept v1.65.0-beta.2 golden baselines уже в истории до ветки задачи). Прочитан #185 (bug, замыкание контура ломается проёмом на участвующей стене) — открыт, без статусной метки S*, что нормально: по AGENTS.md/PROCESS.md §9 issue без статуса вне процесса, а решение владельца прямо говорит, что #185 закрывается кодом #132 и тем же код-ревью — отдельная метка статуса ему на данном этапе не нужна.
  3. Построчно сверены обязательные разделы ТЗ (PROCESS.md §7.1) — таблица ниже.
  4. Проверены технические утверждения ТЗ о текущем (уже реализованном) поведении чтением реального кода на этой же ветке, а не поверено на слово:
    • §3 «current opening placement/index принимает только derived room walls» — подтверждено: openingWallIndex() (src/wall-thickness.ts:2059-2092) строит edges только из for (const room of rooms || []) → roomWallProfile(...); partitions в этой функции не участвуют вовсе;
    • §3 «Physical union специально добавляет partitions после opening cuts» — подтверждено: _lightBarriers() (src/houseplan-card.ts:14157-14231) режет walls проёмами (openCuts/passages) и передаёт уже нарезанные walls вместе с сырым (без вычетов) physical в wallBodiesGeometry(...); physicalBodySet()/physicalBodyParts() (src/physical-geometry.ts:108-173) строят тела partitions/drafts/columns и их junction-патчи независимо от openings — ни один вызов не режет их проёмом;
    • §3 «#173 заменил отдельный инструмент "Перегородка" одной цепочкой "Стены"; активные сегменты живут в room_drafts, явное завершение превращает каждый сегмент в partition» — подтверждено кодом (src/houseplan-card.ts:6657 комментарий «Walls: every completed segment is crash-safe in room_drafts until an […] finish») и текстом docs/USER-GUIDE.ru.md:282-315 («Выберите Стены… сегменты сохранятся обычными независимыми стенами»), а также docs/CANVAS.md разделами «Architectural connection overlay» и «Planar wall faces» (эти разделы явно добавлены после #173 и описывают именно ту архитектуру, на которую опирается ТЗ #132);
    • §11/#185 «room-face detection режется опенингом» — подтверждено: buildPlanSnapGeometry() (src/plan-snap-overlay.ts:118-132) строит sources из roomEdges(...) и явно передаёт cuts: roomCuts на каждый сегмент — то есть сегодня граф для замыкания комнаты режется по opening cuts, что и есть причина #185; docs/CANVAS.md раздел «Planar wall faces» прямо говорит «any physical gap — including an opening cut — remains a gap» про действующее поведение;
    • OpeningCfg (src/types.ts:169-181) сегодня не содержит host — подтверждено; заявление ТЗ §7 «получает optional host discriminator» корректно описывает это как новое поле, а не переименование существующего.
  5. Сверены type-specific light-правила §13 ТЗ (door/gate/passage прозрачны при полу с обеих сторон, window всегда opaque, source внутри exterior opening/window fail-dark) с docs/LIGHT.md («Deliberately opaque…», «The classifier is an explicit door | gate | passage allowlist») — ТЗ не придумывает новую световую семантику, а переиспользует существующую type-specific политику один в один для нового host kind.
  6. Сверены sun-правила §14 («partition window не создаёт exterior wedge») с docs/SUN.md («For every opening of type "window" sitting on an EXTERIOR wall… windows on interior walls do not participate») — совпадает, это не новое правило, а прямое следствие уже принятого канона.
  7. Сверена толщина/cut-геометрия §10 (1–100 см для partition, jamb returns, composite cut только для collinear-покрывающих тел) с docs/WALL-THICKNESS.md §9 («1–100 cm for draft and partition segments», «unioned with room-wall bodies only after door/window/gate cuts») — совпадает дословно.
  8. Сверена терминология с docs/USER-GUIDE.ru.md: «Стены» (§8, «Выберите Стены»), «Проём» (§9, подменю «Окно / Дверь / Открытый проём / Ворота»), host kind «Перегородка» (уже используется в §8 таблице инструментов и в контекстной панели, см. заголовок скриншота «Выбранная перегородка»); ТЗ не вводит новых, не согласованных с гайдом слов.
  9. Проверено docs/CONFIG-COMPATIBILITY.md — раздел «Open-passage opening type (#157)» уже фиксирует, что passage запрещает contact/lock/invert/ flip_h/flip_v даже при null/false; ТЗ §4 п.1 и §15 корректно этого не меняют и не противоречат зарегистрированной схеме совместимости.
  10. Проверено docs/TOUCH-SUPPORT.md на предмет обязательного заявления Touch editor: supported / best effort / not exposed для новой editor feature — в тексте ТЗ такого явного маркера нет (см. находку Low-2).
  11. Проверены трейлеры коммитов b9bf210/2aaabc4 (Issue: #132, User-Visible: no — верно для документации ТЗ) и двусторонняя ссылка issue ↔ ТЗ в docs/specs/README.md:91.
  12. Не запускал автотесты и не собирал бандл — на этапе spec это не требуется; факты о существовании кода и функций проверены чтением файлов на диске, не исполнением.

Обязательные разделы (§7.1 PROCESS.md)

Раздел Есть Комментарий
Сценарий (персона/поверхность/момент) ✅ §1 — администратор, инструменты «Стены»/«Проём», Plan editor (поверхность называется через инструменты, не текстом «Редактор плана», но однозначно определяется)
Что человек увидит до/после (без терминов реализации) ⚠️ §2 — см. Low-1: использует внутренний термин «host-сегмент»
Проблема и связь со scope ✅ §3, с подтверждённым построчно техническим диагнозом
Скоуп / не-скоуп ✅ §5 / §6, оба конкретны и проверяемы
Контракт поведения ✅ §7–17: модель данных, resolver, placement, толщина/cut, room topology (#185), floor/tunnel, light, sun, HA state/actions, move/edit/delete, orphan
UX ✅ §9 (placement), §16 (move/edit/delete)
i18n / accessibility ⚠️ §19 — строки описаны по смыслу, но не перечислены как конкретные ключи en+ru; см. Low-3
Touch ⚠️ затронуто по существу (§9, §21, AC10), но нет обязательного по docs/TOUCH-SUPPORT.md явного маркера Touch editor: …; см. Low-2
Модель данных и миграция ✅ §7, §18 — explicit host discriminator, backward compatibility, no schema migration
Критерии приёмки AC1…ACn с доказательством ✅ §20, 12 штук, у каждого назван тип доказательства (unit/backend/smoke/golden/reviewed golden)
План автотестов ✅ §21 — Unit/Backend/Browser smoke/Golden/Performance, конкретные сценарии
Риски ✅ §23, 9 пунктов с мерами (без явной ссылки на закрывающий AC — необязательно по §7.1, но снижает читаемость)
Откат ✅ §23, последний абзац
Release-артефакты ✅ §24 — оба changelog, USER-GUIDE.ru.md, шесть канонических документов, TESTING.md

Все обязательные по PROCESS.md §7.1 разделы присутствуют и содержательны. Дополнительно есть §25 «Принятые технические предположения», корректно отделяющий свободно изменяемые технические решения от продуктовых решений владельца.

Находки

Находок уровня High и Medium нет.

Low-1 — §2 использует термин реализации «host-сегмент»

Файл: docs/specs/132-partition-openings.md:22

Раздел «Что человек увидит до и после» обязан по PROCESS.md §7.1 формулироваться «одной фразой, без терминов реализации». Текущая формулировка — «выбранный door/window/gate/passage вырезает её тело, следует за конкретным host-сегментом и ведёт себя как тот же тип проёма в обычной стене» — использует «host» и «host-сегмент», термины модели данных этого же ТЗ (§7), которых нет ни в docs/USER-GUIDE.ru.md, ни в обычной речи пользователя. Человек не думает про «host» — он видит, что дверь/окно теперь можно поставить на перегородку и она держится на своём месте при переносе стены.

Почему не блокирует: раздел присутствует, разбит на «до» и «после», и по существу корректен; страдает только буквальное соответствие «без терминов реализации». AC и контракт поведения (§7–17) не зависят от этой фразы.

Решение ревьюера: Low, не блокирует. Рекомендация — заменить «host-сегмент» на «эту перегородку» при следующей правке; можно также оставить как есть с записью здесь, так как продуктовый смысл раздела не искажён.

Low-2 — нет обязательного маркера Touch editor: … по docs/TOUCH-SUPPORT.md

Файл: docs/specs/132-partition-openings.md (раздел §9/§21, отсутствует явное заявление)

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.» ТЗ #132 добавляет новую функциональность Plan-редактора (placement/drag/delete проёма на перегородке) и по существу описывает touch-поведение («Touch placement — best effort; pointercancel/multi-touch не сохраняют draft», AC10, browser smoke «touch cancel/pinch safety»), но нигде не даёт этой ровно сформулированной строки.

Почему не блокирует: содержательно контракт уже соответствует best-effort политике редакторов (docs/TOUCH-SUPPORT.md: «Plan editor: Best effort»), никакого расхождения с политикой нет — не хватает только формальной декларативной строки, которую сам канон требует именно текстом. View/kiosk (обязательная touch-поверхность) в этом ТЗ — чисто presentation (рендер уже существующим пайплайном через общий resolver, AC7), интерактива там не добавляется.

Решение ревьюера: Low, не блокирует. Рекомендация — добавить строку Touch editor: best effort / intentionally degraded в §9 или §21 при следующей правке.

Low-3 — i18n-раздел не перечисляет конкретные ключи en/ru

Файл: docs/specs/132-partition-openings.md:330-342

DoR-чеклист (PROCESS.md §2.5) требует на входе в «Готово к разработке»: «i18n: ключи en + ru перечислены». §19 ТЗ описывает нужные строки по смыслу («Стена или перегородка» в placement guidance/error», host kind «Перегородка» и т.д.), но не называет литеральные идентификаторы ключей (например opening_no_wall_or_partition, opening_host_kind_partition).

Почему не блокирует: выбор конкретных строковых констант — техническое решение (именование), прямо подпадающее под §25 «точная форма discriminator fields может меняться на ревью» по духу того же принципа: разработчик и ревьюер кода решают его сами, без продуктового смысла. Из текста однозначно понятно, какие строки нужны и где.

Решение ревьюера: Low, не блокирует. Рекомендация — при переводе issue в S5-ready дописать в §19 (или в отдельном комментарии) точные ключи, чтобы DoR-пункт был закрыт буквально, а не только по духу.

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

  • Соответствие docs/SCOPE.md. Функция закрывает J4 («от нуля до рабочего плана без внешнего SVG/YAML») и корректно поддерживает J1/J2/J3 через сохранение геометрии/contact/lock/actions идентичными room-wall contract — ни один пункт «Out of scope» не задет, passage не создаёт новую семантику (§6 явно это исключает), lock-инвариант не расширяется (§15: «Door/gate lock action остаётся единственной sanctioned opening surface», «Passage остаётся inert»).
  • Продуктовые вопросы владельцу заданы корректно и по существу. За три раунда (Q1–Q3, затем Q4–Q5) все вопросы — это «что человек видит/делает» (какие типы проёма разрешить, что видно при переносе/удалении перегородки, нужен ли отдельный chooser при совпадении стен) или «сколько видимых изменений входит в issue» (включать ли уже реализованный passage, сворачивать ли фикс #185 в этот же issue) — ни одного чисто технического вопроса владельцу не передано; каждый вопрос шёл с предлагаемым default. Владелец принял все defaults без правок.
  • Технические решения по существу верны и отделены от продуктовых. Обширный раздел «Актуализация после #173/#157» (комментарий 2026-08-19) и итоговая ревизия ТЗ корректно диагностируют, что изменилось в кодовой базе после #173/#157/#185 и что это означает для #132 — проверено построчным чтением реального кода (см. «Как проверялось» п.4–7): расхождений между заявленным и действительным поведением не найдено. Раздел §25 явно маркирует свободно изменяемые технические предположения (форма host-поля, имя resolver'а) отдельно от settled-решений владельца (§4) — никакая догадка не выдана за факт без пометки.
  • AC1–AC12 однозначны и у каждого указан тип доказательства из допустимого по DoR перечня (unit/backend/smoke/golden/reviewed golden). AC5 отдельно требует production-bundle smoke, который «краснеет на origin/dev» — то есть автор заранее закладывает воспроизводимость регресса #185 тестом, который умеет падать до фикса; это ровно тот стандарт доказательства, который код-ревью потребует на следующем этапе (AGENTS.md, issue #143 про смок, не умеющий падать).
  • Не-скоуп (§6) корректно отсекает смежные соблазны: новый opening type, несколько host segments на один opening, конверсия старых room-wall openings в partition openings, новый способ завершения Walls chain, изменение light/window semantics, sun rays от внутреннего окна, полная touch parity редактора, свободное удаление host без confirmation — все типичные места, где скоуп мог бы незаметно расшириться.
  • Миграция и совместимость (§18) корректны: старые данные не мигрируют, host — чисто additive optional-поле, поведение room-wall openings без host не меняется; согласуется с docs/CONFIG-COMPATIBILITY.md (существующая запись про passage не противоречит новым правилам).
  • Откат описан симметрично (§23, последний абзац): запрет на создание новых partition-host openings плюс явный запрет тихой авто-конвертации уже сохранённых host-объектов в room-wall openings.
  • Release-артефакты (§24) называют реальные документы, шесть из семи канонических файлов подсистемы (ARCHITECTURE.md, CANVAS.md, UX-MODES.md, WALL-THICKNESS.md, LIGHT.md, SUN.md, CONFIG-COMPATIBILITY.md) плюс USER-GUIDE.ru.md/TESTING.md/оба changelog — соответствует правилу «документация в том же коммите, что поведение».
  • Трассируемость: docs/specs/README.md:91 ссылается на ТЗ и на #185 одной строкой в обе стороны; трейлеры обоих коммитов ТЗ (Issue: #132, User-Visible: no) корректны для чисто документационного изменения.
  • Решение свернуть #185 в #132 — явное решение владельца (комментарий 2026-08-18), а не самовольное расширение скоупа автором; в AGENTS.md/PROCESS.md нет запрета объединять связанный bug в feature issue по решению владельца, а отсутствие статусной метки S* у #185 не создаёт противоречия, так как #185 явно не идёт по процессу отдельно — его код и доказательство целиком описаны AC5/AC6 этого ТЗ.

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

  • Не проверял, что предложенный ResolvedOpeningHost resolver (§8) реализуем без побочных эффектов на существующие _glowClipCache/ OpeningWallIndex кеши — по §25 п.7 это свободно изменяемое техническое решение автора кода, предмет код-ревью, а не ревью ТЗ.
  • Не запускал автотесты, не собирал бандл и не гонял golden/смоки — на этапе spec это не требуется; существование упомянутых модулей и контрактов проверено чтением файлов на диске (см. «Как проверялось» п.4–7), не исполнением кода.
  • Не проверял, сколько именно golden-сцен с перегородками изменится визуально (владелец просил оценить это заранее в первом комментарии) — это в явном виде эксплуатируется через переоценку сложности 8→9/10 и через требование «reviewed Flat/Iso/Glow golden artifacts» (§24); точное число сцен — вопрос реализации и пре-релизного гейта, не ревью ТЗ.
  • Не проверял реализуемость composite room-wall/partition cut в текущей boolean-геометрии (polyclip-ts) на предельных случаях (например, три и более совпадающих тела) — §23 называет это риском («Composite overlap режет nearby/crossing body») с мерой («collinear full-interval coverage + negative units»), достаточной для ТЗ; сама корректность реализации — предмет код-ревью на unit-тестах §21.
  • Не проверял статус issue #185 на предмет корректности процесса еженедельной гигиены (issue без S*-метки дольше некоторого срока) — это вне скоупа ревью ТЗ #132.

Вердикт

Зелёный. High: 0, Medium: 0. Три находки Low (жаргон в §2, отсутствие обязательной по docs/TOUCH-SUPPORT.md строки Touch editor: …, отсутствие конкретных i18n-ключей) — ни одна не блокирует переход в «Готово к разработке»; все три — точечные текстовые дополнения, не меняющие контракт поведения или AC. Технические утверждения ТЗ о текущем состоянии кодовой базы (после #173/#157) проверены построчным чтением реального кода на этой же ветке и подтверждены без расхождений. Продуктовые вопросы за три раунда обсуждения заданы владельцу корректно (что видит/ делает человек, какой объём входит в issue) и закрыты явными решениями; технические вопросы автор решил сам и промаркировал как предположения (§25), не выдавая догадку за факт.