docs: review document for #132

Issue: #132
User-Visible: no
This commit is contained in:
claude[bot]
2026-08-18 21:33:56 +00:00
parent 2aaabc48d6
commit 4f20befd77
+326
View File
@@ -0,0 +1,326 @@
# 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), не выдавая догадку за факт.