Files
houseplan-card/docs/reviews/SPEC-REVIEW-272-r1.md
T
2026-08-23 21:20:28 +03:00

180 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-272-r1
- Issue: [#272](https://github.com/Matysh/houseplan-card/issues/272) — «Multi-wall стыки beta.5 всё ещё оставляют белые треугольные отверстия вне probe #261»
- Этап: ТЗ на ревью (PROCESS.md §2.4)
- Заход: r1 · блокирующих циклов израсходовано 0 из 4 (первый заход, полный разбор)
- Артефакт ТЗ: `docs/specs/272-no-multiwall-holes.md`, коммит `9e4a9470b63718d75cb6e11b13717ccc24cbe4e6` (ветка `issue/272-no-multiwall-holes`)
- Ревьюер: свежая сессия, без устных пояснений автора
## Скоуп ревью
Не «согласиться», а найти, где ТЗ невыполнимо или непроверяемо. Проверено:
соответствие `docs/SCOPE.md`, обязательные разделы §7.1 PROCESS.md, однозначность
и доказательность каждого AC, отсутствие догадок, выданных за факт, и совпадение
технических утверждений о коде с реальным `src/wall-thickness.ts`.
## Как проверялось
- Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` (§§1–14 целиком) —
подтверждён формат вердикта, лимит циклов, обязательные разделы ТЗ.
- Прочитаны тело issue #272 и оба комментария (аналитика владельца, «ТЗ готово
к ревью»). Issue не помечен `small` → ТЗ обязано жить в `docs/specs/`, что и
сделано.
- Прочитан канонический `docs/WALL-THICKNESS.md` целиком и сверен построчно с
утверждениями ТЗ о контракте multi-wall bevel (`R = 1.25 × H`, `MITRE_LIMIT`,
#249/#261/#197 инварианты).
- Прочитаны связанные issue #249, #258, #261 (закрыты), #270, #271 (открыты,
`S4-spec-review`, без метки `small`) — сверено, что ТЗ #272 не искажает их
историю и корректно описывает состояние #270 (не слито, без семантики
`enclosedHoles` в коде).
- Прочитан `src/wall-thickness.ts`: константы `MITRE_LIMIT`/`MULTI_WALL_JOIN_LIMIT`
(строки 46, 49), `multiWallBevelTrianglesAt`, `bevelMultiWallBody`,
`bevelMultiWallPaper` (строки 1969–2130) — построчно сверены с разделом 3 ТЗ
(«вычитает full-origin pairwise triangles», «union-ит ray rectangles»,
«добавляет tiny core», «клипует к envelope»). Совпадает.
- `grep` по `demo/golden/matrix.mjs`, `demo/golden/harness.mjs`,
`test/golden-matrix.test.mjs`, `test/wall-thickness.test.mjs` — подтверждено
существование `discardedWedgeProbe`, `retainedWedgeProbe`,
`multiWallJunction`; подтверждено отсутствие `enclosedHoles` где-либо в
дереве (только в тексте issue #270, не в коде) — заявление ТЗ о статусе #270
точное.
- Сверен формат ТЗ с предыдущим ТЗ того же автора по той же подсистеме
(`docs/specs/261-white-wedges-root-cause.md`) как образец принятого прежде
документа — раздел «Acceptance criteria и доказательства» там даёт явную
строку «**Доказательство:**» на каждый без исключения AC (AC1…AC7).
- Гейты этого этапа (ревью ТЗ) не подразумевают прогон кода — код не менялся.
`typecheck`/`test`/`build` не прогонялись, так как это ревью документа, а не
реализации; продуктовый код изменений не содержит (диапазон `origin/dev..HEAD`
— один документ).
## Находки
### Medium (в скоупе) — 1: у большинства AC нет явной строки «Доказательство»
`docs/specs/272-no-multiwall-holes.md`, раздел 8, AC2–AC7. Только AC1 несёт
явную строку `**Доказательство:** test/wall-thickness.test.mjs и анонимная
fixture.`. AC2 («#249 остаётся ограниченным внешним bevel»), AC3, AC5, AC6, AC7
описывают ожидаемое поведение и упоминают артефакты в свободной прозе
(`discardedWedgeProbe`, `retainedWedgeProbe`, golden-сцену, `mutation`), но не
называют явно способ доказательства в терминах §2.5 PROCESS.md
(`unit`/`backend`/`smoke`/`golden`/«ревью кода»). AC4 называет метод
(«Targeted production-bundle smoke») в тексте, но тоже без выделенной строки.
**Почему это находка, а не стиль.** PROCESS.md §2.5 (DoR) требует буквально:
«у каждого [AC] указано, чем он доказывается». Собственный прецедент того же
автора по той же подсистеме, `docs/specs/261-white-wedges-root-cause.md`
(предыдущее принятое ТЗ по multi-wall bevel), даёт явную строку
«**Доказательство:**» на каждый без исключения AC — AC1…AC7. #272 отступает от
уже установленной и принятой конвенции без объяснения. Без явной строки
«Готово к разработке» (§2.5) невозможно механически сверить, что каждый AC
имеет названное доказательство — а не восстановить его по смыслу абзаца, что и
есть определение непроверяемости, которое ревью ТЗ обязано ловить.
**Воспроизведение:** сравнить `docs/specs/272-no-multiwall-holes.md:186-245`
(AC1–AC7) с `docs/specs/261-white-wedges-root-cause.md:211-291` (AC1–AC7 того
же автора, та же подсистема) — расхождение в формате видно построчно.
**Исправление:** добавить к AC2, AC3, AC4, AC5, AC6, AC7 явную строку
«**Доказательство:** …», называющую конкретный тест-файл/гейт (по образцу AC1 и
по образцу #261), не переписывая уже верное содержание критериев.
### Medium (в скоупе) — 2: «объявленный exterior sector» не определён алгоритмически
`docs/specs/272-no-multiwall-holes.md`, §6.3, пункт 3: «пустые samples,
связанные с границей окна **или с объявленным exterior sector**, считаются
внешним фоном». Раздел 6.3 — это ядро контракта: он формально отделяет
законную внешнюю пустоту (после bevel #249) от настоящей дыры внутри стыка,
то есть именно то различие, ошибка в котором была причиной, по которой #261 и
старый golden-порог пропустили дефект (см. #270). Первая часть критерия
(«связаны с границей окна») — детерминированный flood-fill, вычислимый
геометрически и без произвола. Вторая часть, «объявленный exterior sector», не
определена: неясно, вычисляется ли она автоматически из угловой геометрии
узла (`node.rays`, разрыв между соседними лучами за пределами `R` — то, что
уже строит `multiWallBevelTrianglesAt()`), или назначается вручную на fixture,
по аналогии с уже существующими вручную объявленными `discardedWedgeProbe` /
`retainedWedgeProbe`.
**Почему это важно именно здесь.** Если «объявленный сектор» — вручную
назначаемый параметр теста, то AC1 (автоматический vector inventory по
minimized fixtures из настоящих экспортов с 12–14 узлами) либо не масштабируется
без ручной разметки каждого узла, либо тест может «объявить» настоящую дыру
законным внешним сектором и молча её принять — то есть повторить ровно тот
класс слепоты, который эта задача должна закрыть (мера #270: seen-but-under-
threshold). Если это вычисляемая величина — соответствующее правило (например,
«угловой разрыв между соседними лучами за пределами retained-overlap `R`»)
стоит явно назвать в тексте контракта, а не оставлять читателю выбирать между
двумя разными по надёжности реализациями.
**Исправление:** одна фраза в §6.3.3, фиксирующая происхождение «exterior
sector» как вычисляемой из геометрии узла величины (или прямая ссылка на то же
правило связности, что уже сформулировано в §6.1), либо явное решение
исполнителя занести в блок §13 «принято предположительно» с формулировкой,
исключающей произвольное ручное объявление на реальных, не golden, fixtures.
### Low — 1: неполное попадание в Core user jobs (снято, без правки документа)
Аналитика владельца и §1 ТЗ ссылаются на J1 **и** J6 (`docs/SCOPE.md`). J6
описывает эволюцию плана (drag/resize/merge/split, multi-client sync,
optimistic locking) — этот баг чисто визуальный и в J6 не попадает
содержательно; корректна только ссылка на J1 («живой обзор... сейчас»). Не
блокирует и не меняет объём работы ни на строку — оставляю без правки
документа с этой записью.
## Что проверено и корректно
- **Формат и полнота разделов** (§7.1 PROCESS.md): сценарий/персона, «что
человек увидит до/после», проблема, scope/не-scope, контракт поведения,
UX/touch/i18n/данные и миграция (все явно «не меняются»), AC1–AC8, риски,
откат, release-артефакты — все присутствуют и совпадают по структуре с
принятым прежде ТЗ #261 той же подсистемы.
- **Технические утверждения о коде не являются догадкой.** Раздел 3 (зона
причины: `multiWallBevelTrianglesAt`/`bevelMultiWallBody`/`bevelMultiWallPaper`)
и раздел WALL-THICKNESS.md (`R = 1.25 × H`, `MITRE_LIMIT = 4`) построчно
сверены с `src/wall-thickness.ts:46,49,1969-2130` — совпадают.
- **История связанных issue не искажена.** #249/#258/#261 закрыты и не
переоткрываются (#272 — новый issue, верно); #270 корректно описана как не
слитая инфраструктурная основа без продуктового решения; #271 — как
рекомендованный, но не обязательный порядок мержа, зафиксированный в §13 как
явное техническое предположение с указанной причиной.
- **Продуктовых вопросов владельцу нет и не должно быть.** Владелец сам
подтвердил дефект в комментарии-аналитике («видимые белые треугольники —
дефект») и сам написал ТЗ; открытых вопросов, требующих решения «что видит
пользователь», в тексте не осталось — §13 корректно фиксирует только
технические предположения, подлежащие оспариванию ревьюером, а не владельцем.
- **Не-scope согрёт риски**: отказ от `R`/`MITRE_LIMIT`, округление bevel,
Optimize-очистка (#273) явно исключены, что не даёт задаче расползтись на
соседние подсистемы.
- **AC6 (mutation)** ссылается на реальный существующий механизм
`scripts/mutation-gate.mjs` — не изобретённый специально для этого ТЗ
инструмент.
- **Privacy/determinism (AC7)** и запрет на коммит приватных экспортов (§2)
соответствуют `docs/SCOPE.md` («never delete/never leak user data» — здесь
зеркально «never commit private exports») и практике репозитория.
## Чего не проверял
- Не запускал `npm run typecheck`/`npm test`/`npm run build` — на этапе ревью
ТЗ продуктовый код не менялся (диапазон `origin/dev..HEAD` содержит только
документ спеки), гонять их бессмысленно.
- Не пытался воспроизвести приватные `1.json`/`2.json` — они не коммитятся по
условию задачи; принял описанные в issue числа узлов (12/14) и координаты
probe на веру автора аналитики, как и разрешает процесс для зафиксированных
владельцем измерений.
- Не оценивал сложность самого geometric boolean алгоritma, который будет
писать разработчик — ТЗ прямо и правильно оставляет конкретный алгоритм
реализации техническим решением (§13.2), и это его законное место, а не
моя работа на этапе ревью ТЗ.
- Не проверял `docs/ARCHITECTURE.md`/`docs/TESTING.md` целиком, только что они
существуют — они процитированы ТЗ как канонические документы, но #272 не
меняет их контракт, только тестовые артефакты, которые эти файлы описывают
на верхнем уровне.
## Вердикт
Жёлтый: AC выполнимы и в основном доказуемы, но раздел 8 нарушает собственную
установленную конвенцию доказательности AC (§2.5 PROCESS.md) на 6 из 8
критериев, а ключевое различие «внешний фон vs дыра» (§6.3.3) содержит один
неопределённый термин ровно в той точке контракта, ради которой заведена вся
задача. Оба High-блокеров нет; обе находки — Medium, в скоупе, чинятся
правкой текста ТЗ без пересмотра архитектуры решения.