# SPEC-REVIEW-249-r2 - Issue: [#249](https://github.com/Matysh/houseplan-card/issues/249) — «Стык трёх стен разной толщины рисуется шипом наружу вместо соединённого узла» - ТЗ: `docs/specs/249-multiwall-junction-bevel.md`, ветка `issue/249-multiwall-junction-bevel`, коммит `675294b9a0c62fb412220bdc04194f6eabedc0c4` - Этап: spec (PROCESS.md §2.4), заход **r2**, блокирующих циклов израсходовано **1 из 4** - Трек: обычный (не `small`) — файл в `docs/specs/` существует - Вердикт: **зелёный** ## Скоуп ревью — по дельте (PROCESS.md §2.10) Предыдущий вердикт найден в комментарии issue от `claude` (2026-08-23T02:18:27Z): жёлтый, заход r1, High 0 / Medium 2 (M1 — отсутствует раздел «Риски», M2 — внутреннее противоречие mitre/bevel в §7.2). **SHA, на котором получен вердикт r1, в самом вердикте не назван** — это находка процесса r1, а не r2; я восстановил его по цепочке коммитов: документ ревью `748d484` («review document for #249») зафиксировал `docs/reviews/SPEC-REVIEW-249-r1.md` с явной строкой «ТЗ: … коммит `8d2e00bbbfbb9151ca2079eaf6e51f916cb59eff`», и по времени (`8d2e00b` — 05:07:48, вердикт — 02:18:27 UTC = 05:18:27 MSK, `748d484` — 05:18:37 MSK) это единственный кандидат. Далее — SHA r1. Дельта раунда: `git diff 8d2e00b..675294b -- docs/specs/249-multiwall-junction-bevel.md`. Дельта **локальна**: изменена статусная строка шапки, переписан §7.2 (пункты 1–2, добавлены пункты 3–4), добавлен новый раздел 11 «Риски и меры снижения» (R1–R5), старые §11 «Откат» и §12 «Предположения» сдвинуты на §12/§13. Ни сценарий, ни AC, ни scope/не-scope, ни термины §6, ни §7.1/7.3/7.4/7.5, ни раздел 10 не тронуты. Смены контракта поведения, ребейза или новой подсистемы нет — правило mitre/bevel то же самое (`d <= R` → митра, `d > R` → фаска), неоднозначная полоса убрана, а не расширена. Поэтому разбор — по дельте, не заново. ## Закрытие раунда r1 | Находка r1 | Чем закрыта | Где это видно | |---|---|---| | **M1** — обязательный раздел «Риски» отсутствовал полностью | Добавлен раздел 11 «Риски и меры снижения» с пятью пунктами (R1 blast radius T-стыков, R2 щель/ложная кладка, R3 порядок/winding/scale, R4 boolean-ошибка, R5 перформанс), каждый с явной митигацией | `docs/specs/249-multiwall-junction-bevel.md:297-350` (см. `git diff 8d2e00b..675294b`, добавленные `+### R1.` … `+### R5.`); R1 прямо называет тот самый риск, что был предметом M1: «Сегодня визуально приемлемая митра в диапазоне `(1.25×H, 4×H]` станет фаской… изменение может затронуть golden-сценарии, не связанные с #249», митигация — обязательный полный `golden:verify` перед бетой с просмотром **всех** изменённых кадров, а не только нового crop | | **M2** — правила §7.2.1/7.2.2 противоречили друг другу в полосе `(R, R+epsilon]` | Оба правила переписаны на единственную границу `R`: п.1 «остаётся митрой, если его **конечное** расстояние от node `d <= R`», п.2 «если пересечения нет, оно не конечное либо `d > R` — фаска». Добавлен явный п.3: «epsilon не расширяет область выбора митры: branch decision всегда разделён единственной границей `R`. Допуск epsilon используется только при проверке и сравнении уже вычисленных floating-point координат» | `docs/specs/249-multiwall-junction-bevel.md:138-151`; выбран ровно вариант закрытия, предложенный r1 («либо оба правила используют R, а epsilon остаётся только допуском сравнения») | Оба закрытия проверены по тексту диффа, не по заявлению автора комментария. ### Побочная проверка: делает ли фикс M2 что-то ещё непоследовательным §7.5 «Ошибки и fallback» (не тронут дельтой) уже содержал формулировку «если точная митра не укладывается в `R`, результатом служит фаска» — то есть до фикса M2 этот раздел молчаливо предполагал границу `R`, а §7.2 колебался между `R` и `R+epsilon`. Новая редакция §7.2 приводит оба места к одному порогу без дополнительной правки §7.5 — расхождение снято, а не создано новое. AC1 (§9, не тронут) по-прежнему использует `1.25×H + epsilon` как допуск **проверки** результата (п.4 нового §7.2), а не как границу branch decision — противоречия между AC1 и переписанным §7.2 нет. Внутренние перекрёстные ссылки (`§7.5`, `§8` — единственные, что встречаются в тексте документа) не задеты сдвигом нумерации §11→12, §12→13; проверено `grep -n "§[0-9]"` по всему файлу. ## Унаследовано из r1 Без повторной проверки в этом раунде приняты выводы `docs/reviews/SPEC-REVIEW-249-r1.md` (коммит ТЗ `8d2e00b`, документ опубликован коммитом `748d484`): - сценарий и соответствие J1 (`docs/SCOPE.md`); - подтверждение корневой причины по коду на `HEAD` (`MITRE_LIMIT = 4` в `src/wall-thickness.ts:46`, независимые митры двух комнат в `insetContour()`/`outsetContour()`, строки 1030/2238/701/1853; `wall-merge.ts` `junctionAt()`/`spaceMergeGeometry()` действительно не формируют этот узел); - согласованность чисел `H = 4.8611`, спайк `8.7312 ≈ 1.80×H`; - перенос решений владельца Q1/Q2 в §4 без искажений; - Scope/не-scope §5; - термины §6 (`ray`, `node`, `join vertex`), обоснованные кодом (`Math.max` при слиянии дублей, строка 626); - полнота и проверяемость AC1–AC7 (таблица AC из r1) — дельта их текст не меняла; - соответствие терминологии `docs/USER-GUIDE.ru.md` («стык», «митра/фаска»); - Low-находка о нестандартной структуре заголовков — принята как снятая ревьюером r1 без требования правки, автор её и не трогал. Эти пункты не переверялись повторно, так как дельта r2 их не касается. ## Что проверено в этом раунде (сверх таблицы закрытия) - DoR-чеклист §2.5 PROCESS.md: «риски перечислены» и «открытых продуктовых вопросов нет» — оба условия теперь выполнены (риски — новый §11; открытых вопросов в тексте r2 не появилось, оба Q1/Q2 остались решёнными в §4). - Новый раздел рисков внутренне согласован с остальным ТЗ: R1↔AC3 (двухлучевые expected values не меняются), R2↔AC1/AC2 (ненулевая связность, отсутствие hole), R3↔AC2 (матрица permutation/winding/`coordScale=1000`), R4↔§7.5 и regression #197, R5↔§8 (`O(E)`/`O(E log E)`) и обязательный предрелизный performance smoke. Ни один риск не изобретён — все пять уже фигурировали как отдельные абзацы аналитики/issue до этого ТЗ (см. первый комментарий владельца в issue: «Риск: 6/10… ошибка способна дать щель, лишнюю кладку либо изменить обычные двухлучевые углы»), r2 их структурировал под требуемый раздел, а не придумал заново. - Тонкая граница `d <= R` вместо `d <= R + epsilon` не расширяет, а сужает зону неоднозначности до нуля — это не новый риск детерминизма, а устранение единственного такого риска, названного в M2. ## Чего не проверял - Не запускал `typecheck`/`test`/`build` — класс A (`src/**`) в дельте r1→r2 не менялся, дифф ограничен `docs/specs/249-multiwall-junction-bevel.md`. - Не переисполнял AC1–AC7 (соответствующих файлов тестов/smoke/fixture ещё не существует — ожидаемо для этапа ТЗ, будет предметом код-ревью). - Не проводил повторный полный разбор сценария, root cause, scope и терминов — см. раздел «Унаследовано из r1»: делта их не касается, значит не задевает и их доказательства. ## Итог Обе Medium-находки r1 закрыты точечно и именно так, как предлагало ревью: раздел «Риски» добавлен по существу (не формальная заглушка — пять пунктов с митигациями, согласованных с остальным ТЗ), а противоречие mitre/bevel снято единой границей `R` с epsilon только как допуском проверки. Дельта раунда локальна, не затрагивает AC/scope/термины и не создаёт нового расхождения. Открытых High/Medium находок нет. **Вердикт: зелёный · заход r2 · блокирующих циклов 1/4 · High: 0 · Medium: 0**