# SPEC-REVIEW-271-r1 Issue: #271 — «Degree-3 узел достраивает короткий луч до 8×H и рисует несуществующую стену и тень» Этап: spec (PROCESS.md §2.4) Заход: r1 · блокирующих циклов израсходовано 0 из 4 ТЗ: `docs/specs/271-finite-multiwall-rays.md`, SHA `482b2be512b38cadb40855b79b710f6602d5eb54` ## Скоуп разбора Первый заход — разбор полный. Проверено: `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` (включая §2.4, §7.1, §4), тело issue #271 и оба комментария (аналитика + «ТЗ готово к ревью»), канонический `docs/WALL-THICKNESS.md`, сам ТЗ, исходный код `src/wall-thickness.ts` (интерфейс `MultiWallNodeRay`, `buildMultiWallNodeMap`, `bevelMultiWallBody`, константы `MITRE_LIMIT`, `MULTI_WALL_JOIN_LIMIT`), связанные issue #249/#261/#270/#272/#273 (заголовки и статусы — все закрыты либо остаются открытыми отдельными задачами, ни одна не дублирует #271). ## Как проверялось 1. Сверил числа из таблицы issue/ТЗ (реальная длина луча vs. текущий rebuild) с формулой в коде: `radius = MITRE_LIMIT * node.halfDepth + eps*2`, `extent = radius * 2` (`src/wall-thickness.ts:2032-2033`) — при `MITRE_LIMIT=4` это ровно `8×halfDepth`. Пример узла 2 `(2404.167, 1245.833)`: `H=62.5` → `8×62.5=500.000`, совпадает с «Текущий rebuild» в таблице ТЗ. Формула и цифры не выдуманы, а прослеживаются до реального кода. 2. Проверил, что `MultiWallNodeRay` (`src/wall-thickness.ts:51-54`) действительно хранит только `{u, halfDepth}` без длины — корневая причина, заявленная в §3 ТЗ, подтверждена чтением, не только цитированием issue. 3. Сверил каждый обязательный раздел ТЗ с перечнем PROCESS.md §7.1: сценарий, что человек увидит, проблема, скоуп/не-скоуп, контракт, UX, модель данных/миграция, i18n, AC1…ACn с доказательством, план автотестов, риски, откат, release-артефакты — присутствуют все. 4. Прочитал все девять AC на однозначность и способ доказательства; для каждого проверил, что критерий формулирует наблюдаемое условие (геометрия/присутствие material), а не расплывчатое «стало лучше». 5. Проверил список «не входит» (§5) против реально открытых соседних issue (#272 — белые треугольные дыры вне probe #261; #273 — sub-grid островок при Optimize) — границы скоупа корректны, чужого не захватывают. 6. Проверил путь ТЗ (`docs/specs/271-finite-multiwall-rays.md`) — issue не помечен `small`, файл обязателен и создан по имени `-.md`, что соответствует правилу. 7. Проверил, что упомянутые файлы существуют: `test/wall-thickness.test.mjs`, `docs/ARCHITECTURE.md`, `docs/TESTING.md`, `docs/CONFIG-COMPATIBILITY.md` — все на месте, ссылки не битые. Гейты кода в этом раунде не гоняются — стадия spec, продуктовый код не менялся (единственный коммит раунда — `docs(spec): ...`, класс C). `npm test`/`typecheck` /`build` не относятся к предмету ревью ТЗ и не запускались. ## Находки Нет. Ни одной High или Medium находки — ни в скоупе, ни вне его. Разобрано отдельно и отклонено как находка: - **Полнота списка документации (§9).** ТЗ включает `docs/USER-GUIDE.md` / `docs/USER-GUIDE.ru.md` в список документов на обновление, хотя пользователю не добавляется ни один новый видимый термин или контрол. Это не дефект: раз видимая геометрия меняется (`User-Visible: yes` в §10), а требование AGENTS.md — обновлять UI-терминологию из USER-GUIDE при изменении видимого поведения — включение файла в список консервативно и не создаёт риска; решение оставить формулировку в тексте или сократить список — на усмотрение автора при реализации, Low, не фиксирую отдельно. - **Отсутствие имени конкретного нового smoke-файла в AC4/AC5.** Смок ещё не существует (создаётся в реализации), поэтому называть файл в ТЗ не требуется — PROCESS.md §7.1/2.5 просит указать *категорию* доказательства (`smoke`), а не имя ещё не написанного файла. Не находка. ## Что проверено и корректно - **Корневая причина** (§3 ТЗ) совпадает с кодом посимвольно: `MultiWallNodeRay` теряет длину, `bevelMultiWallBody` перестраивает луч на `8×H`. Не догадка — прослежено до строк кода. - **Сценарий и персона** (§1): администратор нажимает Optimize → Plan/View, видит несуществующую стену/тень — прямая связь с J1 («правдиво показывать дом») и J6 (одна геометрия для всех потребителей), обе строки есть в `docs/SCOPE.md`. - **Скоуп/не-скоуп** (§5) явно исключает соседние открытые дефекты (#272 белые дыры, #273 sub-grid островок, форма #249 bevel, `MITRE_LIMIT`/ `MULTI_WALL_JOIN_LIMIT`) — задача не разрастается на смежные баги. - **Контракт §6** формулирует финитность луча через проверяемые условия: локальная полоса существует только на `t ∈ [0, length]`; правило дедупликации co-directional дубликатов явно запрещает «перекрыть зазор» присутствием соседнего короткого/толстого дубликата (типичная ловушка, которую легко упустить) — прямо закрыто текстом контракта. - **AC1-AC9** — каждый формулирует наблюдаемое состояние геометрии (присутствие/отсутствие material в конкретной точке, конкретные числа `20.833 → не 500`, `12.5 → не 110`) и называет способ доказательства (unit / geometry unit / targeted smoke / golden semantic probe / mutation). AC7 отдельно требует, чтобы мутант **уронил** AC2 или AC4 — то есть тест на фальсифицируемость заложен в само ТЗ, а не оставлен на усмотрение код-ревью. - **Технические предположения (§13)** помечены явно как «assumed, change freely» и действительно являются техническими (внутреннее представление finite support), а не спрятанным продуктовым решением. Предположение про `wall_columns` унаследовано от собственного анализа владельца в комментарии аналитики, а не изобретено автором ТЗ. - **Совместимость/UX/perf/touch** (§7): персистентная модель не меняется, миграции нет, новых контролов/i18n/HA-вызовов нет, touch явно назван («новых keyboard/touch/focus/ARIA... нет»), perf ограничен явным запретом нового глобального `O(E²)` прохода. - **Откат (§12)** запрещает частичный откат только тестов — учтён урок, из-за которого регрессия становится снова невидимой. - Продуктовых вопросов владельцу нет и не должно быть: единственное решение задачи («физическое тело не может продолжаться дальше конца отрезка») уже прямо следует из J1 и не требует выбора между персонами или трактовки пограничного случая. ## Чего не проверял - Не проверял реализацию (её нет — это стадия spec, код не менялся). - Не прогонял `typecheck`/`test`/`build`/`check-docs` — не относится к этому этапу, диапазон изменений раунда состоит из одного документного коммита. - Не воспроизводил приватные `1.json`/`2.json` владельца — они не в репозитории по правилу «не коммитить пользовательские данные»; проверка ограничилась числами, которые ТЗ и issue цитируют, и сверкой формулы в коде. - Не оценивал, оптимальна ли выбранная будущая технической реализация (union конечных spans vs. clipped polygon) — это по §13 оставлено автору реализации и будет предметом код-ревью. ## Вердикт Все обязательные разделы (PROCESS.md §7.1) присутствуют, каждый AC однозначен и называет способ доказательства, корневая причина подтверждена чтением кода, а не выдана за факт без опоры. Открытых продуктовых вопросов нет и не должно быть. **Зелёный.**