# SPEC-REVIEW-276-r1 - **Issue:** https://github.com/Matysh/houseplan-card/issues/276 - **Этап:** ТЗ на ревью (PROCESS.md §2.4) - **Заход:** r1 · блокирующих циклов израсходовано 0 из 4 - **ТЗ:** `docs/specs/276-coincident-partition-reconciliation.md` (commit `a06c94b9`, ветка `issue/276-reconcile-coincident-partition`) - **Вердикт:** красный · High: 1 · Medium: 3 (все в скоупе задачи) ## Скоуп Обычный трек (не `small`), ТЗ — отдельный файл `docs/specs/276-*.md`. Диффом этого раунда является **вся первая редакция** ТЗ (это r1, дельта не применяется — §2.10 не в силе). Продуктовый код не менялся, диапазон правок — только `docs/specs/276-coincident-partition-reconciliation.md` и `docs/specs/README.md` (commit `a06c94b9`, 249 добавленных строк). ## Как проверялось - Прочитаны `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` §2, §4, §7.1, §7.2, §2.5 (DoR). - Прочитано тело issue #276 и все 4 комментария (аналитика, связанные #277/ #278, claim, хендофф). - Прочитан целиком сам документ ТЗ (`docs/specs/276-coincident-partition- reconciliation.md`, 249 строк). - Сверка терминологии и заявленной причины с канонической документацией: `docs/WALL-THICKNESS.md` (модель independent partitions, §9), фрагменты `docs/USER-GUIDE.ru.md` про инструмент «Граница», «Оптимизировать планы» и проёмы на совпадающей независимой стене, `docs/ARCHITECTURE.md` (Undo/Redo стек, Optimize/model-v7). - Сверка упомянутых в ТЗ функций и артефактов с фактическим деревом (`grep`/`find` по `src/**`, `docs/**`): `physicalBodyParts()`, `_boundaryBlocked()`, `_wallUnionGeometry()`, `mergeCollinearPartitions()`, `optimizePlans()`, `PLAN_MODEL_VERSION`, `resolveOpeningWallAssociation`, `test/plan-geometry-preflight.test.mjs` — все существуют, заявленное поведение соответствует коду. - Прочитан код Optimize Undo/Apply (`src/houseplan-card.ts:15120–15208`) для проверки заявленного контракта Undo/Redo (см. находку H1). - Docs-гейты для этого дифф-скоупа (spec-only, класс C) — не запускались: правки не трогают `src/**`, `node scripts/check-docs.mjs` не относится к этому раунду (автор уже отчитался о зелёном прогоне в хендоффе, что достаточно для docs-диффа). `tsc`/`test`/`build` неприменимы — код не менялся. ## Находки ### H1 (High, блокирует). Заявленный Redo для серверной отмены Optimize не существует и не заявлен как новая работа **Где:** §6 «UI и отчёт Optimize», абзац «После Apply»: «Undo одним действием восстанавливает partition и исходный hosted `host`, **Redo снова канонизирует**.» Тот же контракт закреплён в §12 AC7: «Apply — один WS call, Undo/Redo восстанавливают обе формы.» **Как воспроизвести противоречие:** Optimize использует не общий 50-командный `_geometryHistory` (`Ctrl+Z`/`Ctrl+Shift+Z`), а отдельный one-shot серверный snapshot: - `_alignOptimizedConfirm()` (`src/houseplan-card.ts:15120–15150`) после Apply **очищает** `_geometryHistory.clear()` и включает единственный флаг `_canOptimizeUndo` по ответу сервера (`can_undo`); - `_undoPlanOptimization()` (`src/houseplan-card.ts:15169–15208`) на `houseplan/plan/optimize_undo` **снова** зовёт `_geometryHistory.clear()` и ставит `_canOptimizeUndo = false` — после отмены никакого «Redo» не существует: кнопка в блоке Undo Optimize (`src/houseplan-card.ts:16434– 16437`) только одна, `mdi:undo-variant`, без парного redo-действия; - `docs/USER-GUIDE.ru.md:1444` независимо описывает тот же контракт словами для пользователя: «После оптимизации доступна **одна** серверная отмена, но только до следующего изменения плана… Новый edit делает резервную копию оптимизации устаревшей.» Ни там, ни в `docs/ARCHITECTURE.md` нет упоминания Redo применительно к Optimize. Итого: заявление «Redo снова канонизирует» не соответствует ни одному существующему механизму и не помечено в ТЗ как предположение или как новая работа (в §7 «В скоупе» / §9 «Архитектурный контракт» о добавлении Redo-пути для Optimize нет ни слова, в §8 «Не входит» — тоже). Это ровно тот случай, который процесс называет замечанием: «утверждение о поведении, которого нет ни в одном документе и которое не помечено как предположение». AC7 в текущей форме либо недоказуем (Redo нечем закрыть без изобретения новой функциональности за пределами заявленного скоупа), либо реализация втихую досоздаст Redo-путь для Optimize, чего никто не решал. **Требуется:** либо убрать пункт про Redo из §6/AC7 и явно описать, что после Undo Optimize канонизация не восстанавливается автоматически (пользователь может только повторно нажать «Оптимизировать»), либо — если Redo для Optimize действительно нужен продукту — вынести это отдельным продуктовым вопросом владельцу (это расширение существующего контракта one-shot undo, а не деталь реализации). ### M1 (Medium, в скоупе). Раздел «Риски» отсутствует — обязательный пункт DoR PROCESS.md §2.5 требует явно: «открытых продуктовых вопросов нет; **риски перечислены**» — как отдельный обязательный пункт перед переводом в `S5-ready`. В документе нет ни заголовка, ни абзаца, который перечислял бы риски и меры (аналог §11 «Риски и меры» в предыдущем спеке той же подсистемы, `docs/specs/275-multiwall-strip-containment.md:402`). §10 «Производительность» задаёт бюджет как контракт, а не как риск с митигацией; §14 обсуждает только rollback. Ничего не сказано, например, про риск того, что реальные экспорты (на которые ссылается сама issue) содержат ещё не описанные комбинации (частичное перекрытие, разная толщина), которые попадут в «неоднозначный случай» и останутся неисправленными молча — это стоит явно назвать риском с принятой мерой (отчёт Optimize должен это показывать/не показывать?). **Требуется:** добавить раздел «Риски» с перечислением и принятыми мерами. ### M2 (Medium, в скоупе). Не перечислены затронутые файлы и модули — обязательный пункт DoR PROCESS.md §2.5: «перечислены затронутые файлы и модули» — обязательный пункт. В документе нет ни одного упоминания `src/*.ts` (проверено: `grep -n "src/\|\.ts\b"` по файлу спецификации — 0 совпадений), хотя аналитика в комментарии issue #276 уже называет конкретные функции (`physicalBodyParts()`, `_boundaryBlocked()`, `_wallUnionGeometry()`, `optimizePlans()`, `mergeCollinearPartitions()`) и, значит, авторы уже знают, где лежит код. Раздел 9 «Архитектурный контракт» говорит только расплывчато: «Pure helper живёт рядом с Optimize/wall merge либо в отдельном модуле» — этого недостаточно для DoR-чеклиста, который требует список файлов/модулей, а не намерение. **Требуется:** явный список (`src/plan-optimizer.ts`, `src/wall-merge.ts`, `src/physical-geometry.ts`, `src/houseplan-card.ts` — или что решит автор) — хотя бы приблизительный, он и так «принято предположительно, поменять свободно» по §7.1. ### M3 (Medium, в скоупе). Ссылка на несуществующий канонический документ `docs/PLAN-OPTIMIZE.md` Файл упомянут дважды: в шапке («Связано: …, `docs/WALL-THICKNESS.md`, `docs/PLAN-OPTIMIZE.md`», строка 12) и в §14 «Release-артефакты и rollback» как документ, который коммит реализации обязан обновить (строка 228). Проверено: `find docs -iname "*optimiz*"` не находит такого файла — в репозитории существуют только спеки `docs/specs/{198,199,223,248,252,273}- optimize-*.md`, а канонический материал по Optimize фактически распределён между `docs/USER-GUIDE.ru.md` (раздел про «Общие настройки → Оптимизировать планы», строки ~1392–1449) и `docs/ARCHITECTURE.md` (строка 1036: «Optimize Plans performs the equivalent idempotent model-v7 migration»). Утверждение о существовании отдельного канонического файла — факт, поданный без пометки «предположение», и он неверен уже сейчас: если раздел §14 будет исполнен буквально, автор реализации не найдёт файл для правки. **Требуется:** заменить ссылку на реально существующие документы (`docs/USER-GUIDE.ru.md`, `docs/ARCHITECTURE.md`) либо явно решить создать новый `docs/PLAN-OPTIMIZE.md` и пометить это как отдельное техническое решение автора (§7.1 разрешает такие решения принимать самостоятельно, но не молча выдавать несуществующее за существующее). ## Что проверено и корректно - **Первые два продуктовых раздела по сути отвечают на нужные вопросы**, хотя и не оформлены отдельным «Что человек увидит до и после» (как это сделано в `docs/specs/275-*.md`, §4): §1 даёт сценарий и персону, §6 «После Apply» перечисляет наблюдаемый результат (толщина сразу видна, Boundary разрешает virtual, проём не двигается). Контент присутствует, просто размазан по двум разделам и не сведён в одну фразу без терминов реализации — граница между этим и полноценным замечанием тонкая, решил не заводить отдельным пунктом поверх H1/M1–M3, но авторам стоит это поправить попутно. - **Причина дефекта подтверждена документами, а не выдумана.** Root-cause в §2 совпадает и с issue-аналитикой, и с `docs/WALL-THICKNESS.md` §9 («A precisely collinear room wall covering the same interval is cut as a composite; a crossing/nearby body is not») и `docs/USER-GUIDE.ru.md:555` («Если независимая стена точно совпадает со стеной комнаты, House Plan использует её как явную привязку и прорезает единое составное тело») — то есть рендер/cut уже корректно строит составное тело, а баг именно там, где указано: thickness edit, `_boundaryBlocked()`, Optimize dedup. Ни одна упомянутая функция/константа не выдумана — все существуют в `src/`. проверено чтением, не исполнением. - **Условие безопасной канонизации (§4) консервативно и покрыто негативной матрицей** (AC2): partial/longer/shorter/composite/non-shared/virtual/ different-cm/ambiguous-extra остаются без изменений. Это разумный default, не требующий отдельного вопроса владельцу — граница технической, а не продуктовой ответственности (§7.1). - **Скоуп согласован с `docs/SCOPE.md`.** Комментарий аналитики верно относит задачу к J4/J6; View mode не получает новых интеракций, touch-контракт не расширяется (Plan editor остаётся desktop-first) — соответствует `TOUCH-SUPPORT.md` и §7.1 разделу 11 самого ТЗ. - **Связанные issue корректно разведены.** #277 (Resize ambiguity) и #278 (общий wall-union fallback при исчезновении кладки) верно исключены из скоупа (§8) и совпадают с формулировками в комментарии владельца к #276. - **Все AC1–AC14 указывают способ доказательства** (unit/negative-matrix/ golden/smoke/benchmark/review), ни один не оставлен без «Доказательство». - **Производительность и touch закрывают DoR-пункты** явными числами (§10: ≤15% p95 / ≤25 ms) и явным «новых жестов нет» (§11) — нет необходимости возвращать как замечание. - **Preflight-механизм (#199), на который опирается §4.8 и AC9, реально существует и тестируем** (`src/plan-geometry-preflight.ts`, `test/plan-geometry-preflight.test.mjs`) — проверено чтением, план «Injectable unit» правдоподобен. - **i18n:** формат отчёта (два RU/EN счётчика, нулевые строки скрыты) совпадает с уже принятым паттерном Optimize preview в `USER-GUIDE.ru.md`. Конкретные ключи (`gs.*`) не названы буквально — это Low, не блокирует; автор решает при реализации. ## Чего не проверял - Не запускал `npx tsc --noEmit` / `npm test` / `npm run build` / `node scripts/check-docs.mjs` — диапазон коммита r1 не трогает `src/**` и не является кодом; гейты реализации к спек-ревью неприменимы, продуктовый код не менялся. - Не запускал browser smoke / golden / инварианты модели — geometry/wall/ layout ещё не менялись, это фикстуры и код будущей реализации. - Не проверял реальный пользовательский экспорт, упомянутый в issue (владелец не приложил его из-за пользовательских данных) — верю описанию из комментария аналитики, синтетическая fixture в ТЗ (§7, §13) — это корректная замена по правилам issue. - Не оценивал сложность/трудозатраты реализации (7/10 в аналитике) — вне компетенции спек-ревью. ## Вывод High-находка (H1) блокирует переход в `S5-ready`: контракт Redo для Optimize Undo не существует и не заявлен как новая работа, из-за чего AC7 недоказуем как написано. Три Medium-находки (M1–M3) — все в скоупе задачи и чинятся тем же автором в этом же ТЗ без нового issue: отсутствующий раздел «Риски», отсутствующий список затронутых файлов/модулей (оба — обязательные пункты DoR §2.5) и ссылка на несуществующий `docs/PLAN-OPTIMIZE.md`. Открытых продуктовых вопросов владельцу нет — все находки технические, снимаются автором ТЗ.