diff --git a/docs/reviews/SPEC-REVIEW-271-r1.md b/docs/reviews/SPEC-REVIEW-271-r1.md new file mode 100644 index 00000000..5d1934b6 --- /dev/null +++ b/docs/reviews/SPEC-REVIEW-271-r1.md @@ -0,0 +1,126 @@ +# 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 однозначен и +называет способ доказательства, корневая причина подтверждена чтением кода, а +не выдана за факт без опоры. Открытых продуктовых вопросов нет и не должно +быть. + +**Зелёный.**