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

195 lines
18 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-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.