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

16 KiB
Raw Blame History

SPEC-REVIEW-272-r1

  • Issue: #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, в скоупе, чинятся правкой текста ТЗ без пересмотра архитектуры решения.