docs: review document for #309

Issue: #309
User-Visible: no
This commit is contained in:
claude[bot]
2026-08-25 18:11:25 +00:00
parent f0ea376aa9
commit cee4718ffa
+202
View File
@@ -0,0 +1,202 @@
# SPEC-REVIEW-309-r2
Issue: https://github.com/Matysh/houseplan-card/issues/309 «Стыковочные узлы: визуальный лимит mitre и устранение паразитных парных патчей»
Этап: spec (обычный трек — сложность/риск владельца 6/10 и 7/10, `small` невозможен)
Заход: r2 · блокирующих циклов израсходовано 1 из 4 (до этого раунда)
Ревьюер: Claude (сессия ревью ТЗ), артефакт: `docs/specs/309-junction-visual-limit.md`, коммит `f0ea376a` (ветка `issue/309-junction-visual-limit`). `git diff origin/dev...HEAD --stat` — 2 файла документации (спек + `SPEC-REVIEW-309-r1.md`), кода нет.
## Скоуп
Продуктовая рамка не изменилась со времён r1 — рендер кладки в узлах стыка стен
(J1/J6 SCOPE.md), см. `SPEC-REVIEW-309-r1.md`, раздел «Скоуп»: соответствие
подтверждено там, дельта её не задевает, наследую без повторной проверки.
Предмет r2 — ревизия 2 ТЗ (коммит `f0ea376a`), написанная в ответ на жёлтый
вердикт r1: «Взял: … добавлены Сценарий, „до/после“, Скоуп/не-скоуп, UX, Модель
данных (нет миграции), i18n (нет строк), Риски (4), Release-артефакты» (комментарий
владельца от 2026-08-25T18:02:38Z). Разбор по дельте (§2.10): единственная
находка r1 была одна (Medium, 8 пунктов одним списком), делта — точечная правка
того же файла, новую подсистему не задевает, контракт поведения не меняет →
полный повторный разбор не требуется, проверяю дельту плюс всё, до чего она
дотягивается.
## Как проверялось
1. Нашёл вердикт предыдущего раунда и SHA, на котором он получен: комментарий
`IC_kwDOTOcLQM8AAAABQrvNFQ` (2026-08-25T18:01:51Z) — «Вердикт: жёлтый · заход r1
· блокирующих циклов 0/4 · High: 0 · Medium: 1 → в задаче», документ
`docs/reviews/SPEC-REVIEW-309-r1.md`, получен на коммите спека `ff623cd4`
(сам документ это явно называет в шапке — не находка, SHA назван).
2. Объявляю дельту: `git diff ff623cd4..f0ea376a -- docs/specs/309-junction-visual-limit.md`
— 36 вставленных строк, 1 изменённая (строка статуса), 0 удалённых из
существующего текста. Все вставки — новые разделы §0 (Сценарий + до/после) и
§3.6–§3.11 (Скоуп/не-скоуп, UX, Модель данных, i18n, Риски, Release-артефакты).
Разделы §1–§3.5 (проблема, решения владельца, контракт 3.1–3.5), §4 (поверхности),
§5 (AC1–7), §6 (план тестов), §7 (откат) — байт-в-байт те же, что были на
`ff623cd4` и уже проверены в r1.
3. По каждому из 8 пунктов находки r1 построчно сверил новый текст с требованием
(таблица «Закрытие раунда r1» ниже).
4. `git diff origin/dev...HEAD --stat` — только документация (`docs/specs/309-…md`,
`docs/reviews/SPEC-REVIEW-309-r1.md`), кода нет. Как и в r1, `npx tsc --noEmit` /
`npm test` / `npm run build` на этом этапе неприменимы — не гоняю по той же
причине, что и r1 (нет изменений `src/**`), это не пропуск гейта, а отсутствие
предмета для него.
5. Проверял НЕ на веру автору полноту новых разделов, а построчно читал код и
канонический документ подсистемы для проверки: `src/wall-thickness.ts` —
константы `MITRE_LIMIT = 4` (:79), `MULTI_WALL_JOIN_LIMIT = 1.25` (:83, «#249»),
их использования (`:1144` парный mitre, `:2087` веер, `:1475`/`:2642-2666`/
`:3311`/`:3875` — `node.limit`/`multiWallBevelCutsAt`); `docs/WALL-THICKNESS.md`
§3 (:100-230) — механизм #249/#279 и его текущий статус; `docs/TOUCH-SUPPORT.md`
(:1-40) — контракт touch для View/kiosk; `PROCESS.md` §2.5 (DoR) — полный список
обязательных пунктов выхода в «Готово к разработке».
## Закрытие раунда r1
| Находка r1 (подпункт Medium) | Чем закрыта | Где это видно |
|---|---|---|
| Сценарий отсутствует | Добавлен §0 «Сценарий»: персона (владелец дома), поверхности (Plan-редактор для правки, View/kiosk/Static/hidden-Iso для просмотра — с явной ссылкой на общий структурный кэш), момент (схождение стен под острыми углами/у толстых стен/смешанных толщин) | `docs/specs/309-junction-visual-limit.md:7-9` |
| «Что человек увидит до/после» отсутствует | Добавлено в §0: «До: … клинья и пики длиной до 4 полутолщин …» / «После: … вершины не дальше 1.5 полутолщины …» | `:11-12` |
| Скоуп и не-скоуп не выделены явным разделом | Добавлен §3.6 «Скоуп и не-скоуп» | `:56-59` — **закрыта частично**, см. находку ниже |
| UX отсутствует | Добавлен §3.7 «UX»: «никаких новых контролов… форма превью и персиста совпадает» | `:61-63` |
| Модель данных и миграция не названы явно | Добавлен §3.8: «Нет. Чистая рендер-геометрия…» | `:65-67` |
| i18n не названо явно | Добавлен §3.9: «Нет новых строк» | `:69-71` |
| Риски не собраны в раздел | Добавлен §3.10, 4 пронумерованных риска (дыры на фаске, регресс golden, скрытые сектора при удалении пар, перф) | `:73-78` |
| Release-артефакты не выделены | Добавлен §3.11: changelog RU+EN, обновление `docs/WALL-THICKNESS.md` §3, screenshot-fingerprint | `:80-82` |
7 из 8 подпунктов закрыты полностью и корректно. Один («Скоуп и не-скоуп») закрыт
только формально — раздел появился, но не содержит именно то, что r1 просил
назвать явно, — см. «Находки».
## Унаследовано из r1
Без повторной проверки, по документу `docs/reviews/SPEC-REVIEW-309-r1.md` на
SHA `ff623cd4` (дельта эти разделы не касается — сверено побайтово diff'ом
из шага 2):
- Продуктовая рамка/скоуп-соответствие SCOPE.md J1/J6 («Скоуп» r1).
- Контракт §3.1–3.5: `VISUAL_MITRE_LIMIT = 1.5`, парная фаска (§3.2), фильтр
не-соседних пар в узлах ≥3 лучей (§3.3), правило вееров (§3.4), инварианты
«без дыр»/«без фантомов»/превью=персист (§3.5) — построчно проверены на
`src/wall-thickness.ts:80,1082-1150,2087`, `src/physical-geometry.ts:242` в r1.
- Математика порога: для равных толщин под 90° вылет `h·√2 ≈ 1.41h < 1.5h` —
проверено в r1, прямые/тупые углы не задеты.
- AC1–7 (§5) — однозначны, у каждого указано доказательство (юнит/golden/смок),
мутанты (a)-(d) целятся в разные пункты контракта и не дублируют друг друга.
- План автотестов (§6) и откат (§7) — полны.
- Low-находка r1 (расхождение «16» vs «15+1» junction-сцен в
`docs/WALL-THICKNESS.md`/ТЗ) — снята ревьюером без правки в r1, не проверяю
повторно: не влияет ни на один AC.
- Трейлеры документационных коммитов (`Issue: #309`, `User-Visible: no`) —
корректны, проверено в r1 для `ff623cd4`; для `f0ea376a` та же схема (см. «Что
проверено» ниже).
## Находки
### Medium (в скоупе задачи — правится в этом же документе)
**Раздел «Скоуп и не-скоуп» (§3.6) не закрывает находку r1 полностью, и одна из
двух его частей — не мелочь, а связана с реальным соседним механизмом кода.
DoR (§2.5) также требует явного пункта про touch, которого до сих пор нет.**
1. **#249/#279 не исключены явно, хотя r1 просил именно это.** r1 дословно:
«не сказано explicitly, что #249-шеврон для near-orthogonal узлов (#279) …
не пересматривается». В §3.6 не-скоуп перечисляет модель данных, hit-зоны,
«политику полного mitre в допустимом пороге (#302 решение №5)» и #308 — но
не называет #249/#279 вовсе.
Это не формальность: в кодовой базе есть **второй, самостоятельный** лимит —
`MULTI_WALL_JOIN_LIMIT = 1.25` (`src/wall-thickness.ts:83`, «#249»),
применяемый через `node.limit` в `multiWallBevelCutsAt` (:2642-2666, вырез
лишнего клина у экстерьерного полотна/paper envelope) и в mitre-логике
контуров комнат (:1475, :3875, `multiWallNodeAt(...)?.limit`). Число `1.25`
лежит прямо рядом с новым `VISUAL_MITRE_LIMIT = 1.5`, который вводит §3.1.
`docs/WALL-THICKNESS.md:189` подтверждает статус-кво: «A degree-3+ node
closes with a FULL mitre… **the #249 chamfer is retired**» — то есть для
вееров кладки (то, что правит #309) #249-лимит действительно не участвует
(ретировался вместе с #302), но `:120-126` там же говорит, что «the same
bounded rule [R = 1.25·H] applies to the **exterior half-wall and paper
envelope**» и к mitre контуров комнат — то есть механизм жив в двух других
местах, которые используют ТУ ЖЕ структуру `MultiWallNodeMap`/`node.limit`,
что и веер, который правит #309.
Технически ТЗ, видимо, действительно не задевает эти два места (§3.2/§3.4
правят только `linearWallJoinPatches` и `junctionNodeGeometry`), но сам факт,
что рядом с новым порогом 1.5h лежит старый порог 1.25h на **той же** карте
узлов в **соседнем** коде — ровно тот случай, где реализатору легко решить
«раз уж я вижу `node.limit`, поправлю заодно» и задеть paper/room-contour
рендер, которого AC не касаются и golden для него не планируется. Один явный
пункт в §3.6 («#249/`node.limit` — вырез экстерьерного полотна и mitre
контуров комнат — не пересматривается, использует свой независимый
`R = 1.25·H`») закрывает находку r1 по существу, а не только по форме.
2. **Touch impact не назван нигде явным пунктом.** DoR (`PROCESS.md` §2.5)
требует явно: «влияние на touch по `docs/TOUCH-SUPPORT.md` (View и киоск —
блокирующие)» — как отдельный обязательный пункт очереди «Готово к
разработке», не только как часть §7.1. `docs/TOUCH-SUPPORT.md` фиксирует
View и kiosk как **touch-first, полностью поддерживаемые** поверхности —
и ровно они входят в скоуп по §0 (общий структурный кэш). §3.7 UX
ограничивается «никаких новых контролов», что не то же самое, что явный
ответ по touch. Ответ тривиален («чистая рендер-геометрия, никаких
интерактивных элементов не меняется — идентична на touch и mouse/keyboard»),
но раздел должен произносить его явно, а не подразумевать — тот же принцип,
которым в r1 обосновано требование явного i18n/миграции при тривиальном
ответе.
Оба пункта решаются автором самостоятельно, без вопроса владельцу: ни один не
требует продуктового решения — #249/#279 факт выводится из уже читаемого кода
и канонического документа, touch — прямое следствие «нет новых интерактивных
элементов», уже фактически сказанного в §3.7. Без High-находок это жёлтый
вердикт: два предложения добавляются в §3.6, следующий заход проверяется по
дельте этих двух правок.
## Что проверено и корректно
- 7 из 8 пунктов находки r1 закрыты полностью, текстом, который отвечает
требованию r1 буквально (см. таблицу «Закрытие раунда r1»).
- §0 «До/после» использует ту же величину («полутолщина» = `h` = `halfDepth`),
что и контракт §3.1-3.2 и математика AC4 (1.41h < 1.5h) — согласовано, не
разъезжается с техническим текстом.
- §3.8/§3.9 («нет модели данных», «нет i18n») корректны по существу: изменение
чисто рендер-геометрическое, конфиг/экспорт/импорт не трогает ни один AC.
- §3.10 риск 3 («удаление парных патчей в узлах ≥3 лучей может вскрыть непокрытые
сектора») корректно ссылается на §3.3, где то же самое уже названо как решаемое
измерением — согласовано, не новое обещание без опоры.
- §3.11 release-артефакты (changelog RU+EN, `docs/WALL-THICKNESS.md` §3,
screenshot-fingerprint) — полный список по требованию `docs/specs/README.md`;
способ ревью golden (`--reviewed`) уже назван в неизменённом §3.5, повторно
дублировать в §3.11 не обязательно.
- Статус-строка (`Статус: ревизия 2 (после SPEC-REVIEW-309-r1: …)`) корректно
ссылается на r1 — трассируемость ревизии на месте.
- Трейлеры коммита `f0ea376a`: `Issue: #309`, `User-Visible: no` — корректны
для документационной правки (не меняет поведение, меняет только ТЗ).
## Чего не проверял
- `npx tsc --noEmit` / `npm test` / `npm run build` — на этом этапе снова нет
изменений `src/**` (`git diff origin/dev...HEAD` — только 2 файла
документации), гейты кода относятся к этапу код-ревью (§2.7), как и в r1.
- `npm run golden:capture`/`golden:verify` и фикстуру
`test/fixtures/309-junction-teeth.json` — фикстура ещё не создана, план
тестов не менялся дельтой, предмет реализации/код-ревью.
- Реальное поведение `multiWallBevelCutsAt`/room-polygon mitre на конкретном
экспорте владельца — находка выше про #249/#279 основана на чтении кода и
`docs/WALL-THICKNESS.md`, не на исполнении; для проверки корректности самого
этого утверждения о статус-кво это достаточно (речь о том, что ТЗ должно
сказать, а не о поведении будущей реализации), но не является заменой
код-ревью после реализации.
- `docs/USER-GUIDE.ru.md` — как и в r1, не переоценивал: задача не вводит
пользовательских терминов интерфейса (чистая геометрия рендера).
## Вердикт
Жёлтый. High-находок нет. Один Medium **в скоупе** задачи: раздел «Скоуп и
не-скоуп» (§3.6), добавленный в ревизии 2, закрывает находку r1 только
частично — не называет явно, что #249/`node.limit` (вырез экстерьерного
полотна/paper envelope и mitre контуров комнат, `R = 1.25·H`, живой и
независимый от нового `VISUAL_MITRE_LIMIT = 1.5` механизм в соседнем коде той
же карты узлов) не пересматривается; отдельно не назван обязательный по DoR
(`PROCESS.md` §2.5) пункт про touch-impact. Оба пункта решаются автором
самостоятельно, ответы тривиальны, правка — два предложения в том же файле.
Остальные 7 из 8 пунктов находки r1 закрыты полностью и корректно; технический
контракт (§3.1-3.5), AC1-7, план тестов и откат не менялись дельтой и
наследуются из r1 без повторной проверки.