Files
houseplan-card/docs/reviews/SPEC-REVIEW-229-r3.md
T
2026-08-21 09:01:53 +00:00

18 KiB
Raw Blame History

SPEC-REVIEW-229-r3

  • Issue: https://github.com/Matysh/houseplan-card/issues/229
  • ТЗ: docs/specs/229-merge-collinear-partitions.md
  • SHA на момент этого ревью: 1ecd26713882e11760daed80003632d7e8778ba2 (коммит docs: a room junction is a side, not just a corner (#229 r2 M1))
  • Этап: spec (PROCESS.md §2.4) · трек: обычный (не small)
  • Заход: r3 · блокирующих циклов израсходовано 2/4 до этого вердикта
  • Предыдущие раунды:
    • docs/reviews/SPEC-REVIEW-229-r1.md, вердикт жёлтый, SHA 81210f7afc1995b52c031daa1f593f1b54e03f8f
    • docs/reviews/SPEC-REVIEW-229-r2.md, вердикт жёлтый, SHA ecc3d6a88ba259d3446f77ab3a60170337bc068f
  • Ревьюер: Claude, роль «ревьюер ТЗ»

Скоуп этого раунда

Дельта — ровно один коммит, 1ecd267, отвечающий на единственную находку r2 (Medium-1: «ребро комнаты» в §8.2 определено как «ближайшая вершина полигона», а не как отрезок, из-за чего T-стык партиции к середине комнатной стены не считался бы причиной оставить узел). Правка трогает только docs/specs/229-merge-collinear-partitions.md: один абзац §8.2, формулировку AC2, одну новую строку мутационного гейта (§14). Тело issue #229 не менялось (сверено с gh issue view 229 — последний комментарий автора и есть хендофф r2→r3, без изменения текста issue). Метка issue сейчас S4-spec-review, без small/review-4, что подтверждает регулярный трек и лимит 4 циклов.

Дельта локальна (§2.10 PROCESS.md): один коммит, одна секция плюс точечная правка соседних, не ребейз, не смена контракта, не новая подсистема — объём разбора сокращён до находки r2 и того, что она затрагивает (§8.2, AC2, §14); остальное наследуется из r1/r2 без повторного прогона.

Как проверялось

  1. Прочитан вердикт r2 (docs/reviews/SPEC-REVIEW-229-r2.md) целиком — одна Medium-находка (Medium-1), без High.
  2. git diff ecc3d6a..HEAD -- docs/specs/229-merge-collinear-partitions.md — единственный файл дельты, 18 строк; построчно сверен с требованием Medium-1 из r2 («что нужно»: заменить «ближайшая вершина полигона» на «ближайшая точка на любой стороне (ребре) полигона»).
  3. Прочитан комментарий автора в issue (хендофф r2→r3, 2026-08-21T08:56:11Z) — сверен с фактическим диффом, а не принят на слово: текст диффа совпадает с тем, что автор описывает.
  4. Проверены обе фактические ссылки, добавленные правкой:
    • docs/specs/141-wall-junctions.md — новый абзац §8.2 цитирует «§13.1» с фразой «к середине существующей wall/partition». Прочитан весь файл 141 вокруг §4, §13.1 и §13.2 построчно: искомая фраза находится не в §13.1 (строки 332–349, п.5 — «T partition→partition, draft→partition и partition→solid room wall», без слова «середина»), а в §13.2, п.3 (строка 361: «...новую partition через line-snap #137 к середине существующей wall/partition»). Правильный якорь для утверждения «T-стык к комнатной стене — штатный случай продукта» — это §4.1 (строка 60–62: «endpoint↔line (T) соединения... между active/saved draft, partition и готовой комнатной стеной») и §13.1 п.5, которые сам ревьюер верно процитировал в r2. Разбор — находка этого раунда, см. ниже.
    • src/plan-snap-overlay.ts:313 — прочитан файл целиком в окрестности строки; distToSegment([pointer[0], pointer[1]], line) на строке 313 точно совпадает, и line строится из geometry.segments, которые (строка 139) заполняются из roomEdges(options.space.rooms) — подтверждено чтением src/logic.ts (roomEdges, distToSegment, строки 134 и 1937). Примитив «расстояние точки до стороны комнаты» действительно существует и действительно оперирует отрезком, а не вершиной — ссылка точна.
  5. Заново прочитаны §8.2, AC2 (§12) и новая строка §14 — единственные места, которые дельта задевает. Проверено, что формулировка AC2 после правки требует теста именно на T-стык в середину длинного ребра, а не только на угол, и что новый мутант (junction-checks-room-vertices-only) откатывает ровно эту правку («искать примыкание комнаты только по вершинам полигона») — то есть гейт умеет отличить старое (неверное) поведение от нового.
  6. Не перечитывались повторно: §1–§7, §9, §10, §11, §15, §16, §17, AC1, AC3–AC9 — дельта их не касается (см. «Унаследовано» ниже).
  7. Код не запускался, гейты не гонялись — на этапе spec-review реализации нет (как в r1 и r2).

Закрытие раунда r2

Находка r2 Чем закрыта Где это видно
Medium-1 — критерий «ребро комнаты» (§8.2) требовал совпадения с ближайшей вершиной полигона, что пропускало T-стык партиции к середине комнатной стены — канонический случай docs/specs/141-wall-junctions.md §4.1/§13.1 п.5 §8.2 переписан: «Точка стыка берётся у ребра комнаты — ближайшая точка на любой стороне полигона, а не только его вершина». Добавлен поясняющий абзац со ссылкой на канон и на существующий код-примитив distToSegment/roomEdges. AC2 дополнен требованием: случай «ребро комнаты» доказывается T-стыком в середину длинного ребра, тест обязан быть красным, если реализация ищет только вершины. В §14 добавлен мутант junction-checks-room-vertices-only → guard «юнит AC2 (T-стык)» docs/specs/229-merge-collinear-partitions.md §8.2 (абзацы 1–2 после правки), §12 AC2 (предложение о T-стыке), §14 (новая строка таблицы)

Закрыта полностью и по существу: формулировка §8.2 теперь point-to-segment, а не point-to-vertex, ровно то, что требовало «что нужно» в r2. AC2 после правки не найдёт причину оставить узел только по критерию, который сам же и пропускал T-стык — наоборот, AC2 явно требует падения теста на этом сценарии до фикса и держит его зелёным после.

Находки этого раунда

Low-1 — новый абзац §8.2 цитирует §13.1 фразой, которая на самом деле находится в §13.2

Файл: docs/specs/229-merge-collinear-partitions.md, §8.2, второй абзац (внесён дельтой r3).

Формулировка ТЗ: «...штатный случай продукта (docs/specs/141-wall-junctions.md §13.1 прямо называет примыкание «к середине существующей wall/partition»)...»

Проблема. Фраза «к середине существующей wall/partition» находится в docs/specs/141-wall-junctions.md строка 361, это пункт 3 раздела §13.2 («Targeted production-bundle smoke» — сценарий рисования новой перегородки через line-snap #137 к середине существующей стены), а не §13.1 («Unit» — пункт 5 там говорит «T partition→partition, draft→partition и partition→solid room wall», без слова «середина»). Утверждение по существу верное и подтверждённое документом — T-стык партиции к комнатной стене действительно канонический случай продукта, — но подтверждается оно §4.1 (строка 60–62, «endpoint↔line (T)... между... partition и готовой комнатной стеной») и §13.1 п.5, а не тем местом, которое сейчас названо. Сам ревьюер процитировал верные места (§4.1 и §13.1 п.5) в r2 — здесь автор ТЗ, судя по всему, взял идею оттуда, но перепутал номер подраздела и вставил не ту цитату.

Почему это не блокирует. AC2 (§12) самодостаточен: он требует конкретного теста («T-стык в середину длинного ребра», красный при поиске только вершин) и не зависит от того, на какой подраздел ссылается пояснительный абзац. Утверждение при этом не голая догадка без подтверждения — оно подтверждено в том же документе, только другим номером параграфа; я independently перечитал §4.1 и §13.1 весь файл 141 и убедился, что канон действительно требует T-стыка partition↔room-wall. Ревьюер кода не будет ориентироваться на номер параграфа — он будет писать unit-тест по буквальному требованию AC2.

Решение. Снимаю без действия автора: заменить «§13.1» на «§4.1» (или на «§13.1 п.5 и §4.1») можно одной правкой при следующей любой правке этого файла, но отдельного цикла ревью это не стоит — ни AC, ни контракт не затронуты, эффект чисто в точности сноски.

Унаследовано из r1/r2 (без повторной проверки)

Документы: docs/reviews/SPEC-REVIEW-229-r1.md (SHA 81210f7), docs/reviews/SPEC-REVIEW-229-r2.md (SHA ecc3d6a). Принято на веру, так как дельта r3 этих мест не касается:

  • Продуктовая рамка §1–§4 (персона, сценарий, три решения владельца 2026-08-21) — не менялась с r1; тело issue не менялось между r2 и r3 (сверено в этом раунде).
  • §5–§7 (цели, скоуп, не-скоуп), §9 (данные/i18n/a11y), §10 (performance), §15 (release-артефакты), §16 (откат) — не менялись дельтой r3, r2 их тоже не трогал.
  • §8.6 («Что именно сращивается при завершении цепочки», закрытие M1 из r1) и AC8 — проверены r2, дельта r3 их не касается.
  • §8.4 (материализация x/y/angle, закрытие M3 из r1) и AC3 — проверены r2, дельта r3 их не касается.
  • AC1, AC4, AC5, AC6, AC7, AC9 — признаны однозначными и проверяемыми в r1, формулировки не менялись ни в r2, ни в r3.
  • Соответствие обязательным разделам §7.1 PROCESS.md, отсутствие непомеченных догадок за пределами §8.2/§8.4/§8.6 (кроме разобранной здесь мелкой находки) и корректность Touch editor: not exposed по docs/TOUCH-SUPPORT.md §153 — проверены в r1, не переоценивались.
  • Строки кода wall-thickness.ts:1258, plan-optimizer.ts:402-530, align-grid.ts:262, partition-openings.ts:43-90, wall-thickness.ts:743, :2256, houseplan-card.ts:6538, :7824, :12007, проверенные в r1/r2, — дельтой r3 не менялись, не перечитывались повторно.
  • Согласованность с docs/USER-GUIDE.ru.md §8 — не переоценивалась, дельта её не касается.
  • «Чего не проверял» из r2 (значения EPS_ANGLE/EPS_JOIN как числа — осознанно не зафиксированы; критерий «колонна — центр» — не подтверждён и не опровергнут, останется открытым до код-ревью) — остаётся в той же формулировке, дельта r3 этого не касается.

Что проверено и корректно (дельта r3)

  • Medium-1 из r2 закрыта по существу текстом ТЗ (не заявлением автора) — см. таблицу закрытия выше. Формулировка §8.2 теперь point-to-segment, ровно то, что было запрошено.
  • AC2 после правки требует падения теста на T-стыке в середину ребра при реализации, ищущей только вершины — дисциплина «тест должен уметь падать» сохранена.
  • Новая строка мутационного гейта (junction-checks-room-vertices-only) корректно привязана к AC2 и откатывает именно правку этого раунда.
  • Ссылка на код plan-snap-overlay.ts:313 точна и подтверждена чтением файла и src/logic.ts (roomEdges, distToSegment) — примитив «точка-к-отрезку» для рёбер комнаты действительно существует в кодовой базе.
  • Продуктовая рамка не расширена и не сужена этой правкой: изменение чисто геометрическое (точка стыка — отрезок, а не вершина), пользователь не видит разницы между «стык у угла» и «стык посередине стены», оба должны одинаково сохранять узел — не продуктовый вопрос, решён ревьюером r2 по инструкции.

Чего не проверял

  • Реализацию — её не существует на этом этапе.
  • Продуктовую рамку и решения владельца §1–§4, §8.4, §8.6, AC1, AC3–AC9 — не менялись дельтой, наследуются из r1/r2 (см. выше).
  • Существующие смоки и docs/specs/README.md — вне зоны этого ревью и не затронуты дельтой, как и в r1/r2.
  • Критерий «колонна — центр» — остаётся открытым предположением из r2, дельта r3 его не касается, всплывёт на код-ревью через AC2, если окажется неверным.
  • Точный номер параграфа в docs/specs/141-wall-junctions.md, куда стоило бы переставить цитату (см. Low-1) — снято решением ревьюера без требования правки, дальше не разбиралось.

Вердикт

Единственная находка предыдущего раунда (Medium-1) закрыта полностью и по существу текстом ТЗ, а не заявлением автора. Единственная новая находка этого раунда — Low (неверный номер подраздела в пояснительной ссылке, без влияния на AC или контракт) — снимаю с записью, без возврата на правку. High нет, Medium в скоупе нет. Итог — зелёный: ТЗ готово к разработке (S5-ready). Зелёный вердикт бюджет циклов не тратит (§4, #227) — израсходовано 2/4.