mirror of
https://github.com/Matysh/houseplan-card
synced 2026-10-04 05:41:34 +00:00
327 lines
30 KiB
Markdown
327 lines
30 KiB
Markdown
# 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), не выдавая догадку за факт.
|