# SPEC-REVIEW — issue #478, заход r1 - **Issue:** https://github.com/Matysh/houseplan-card/issues/478 - **Этап:** spec (PROCESS.md §2.4) - **ТЗ:** `docs/specs/478-remove-room-drafts.md` - **Ветка/SHA:** `issue/478-remove-room-drafts` @ `30a16ed36461246e06d395f2bab7f4b6d9e12eaa` - **Заход:** r1 · блокирующих циклов израсходовано 0 из 4 (первый заход, бюджет §4 не тратится) - **Вердикт: зелёный** ## Скоуп ревью Задача не помечена `small` (метки: `P2`, `S4-spec-review`, `tech-debt`; автор сам классифицировал её как полный трек — миграция схемы v9→v10, несколько поверхностей, изменение контракта сохранения незамкнутой цепочки). Поэтому ТЗ обязано жить в `docs/specs/`, что и сделано; файла лишнего в теле issue нет. Диапазон изменений этого раунда — только `docs/specs/478-remove-room-drafts.md` и одна строка в `docs/specs/README.md` (класс C, документация, ожидаемо для этапа spec): ``` docs/specs/478-remove-room-drafts.md | 531 +++++++++++++++++++++++ docs/specs/README.md | 1 + ``` Продуктового кода в ветке нет — это правильно для стадии `S4-spec-review`. ## Как проверялось 1. Прочитан `docs/SCOPE.md`, `AGENTS.md`, `PROCESS.md` §2.4/§7.1/§4/§7.2/§12. 2. Прочитано тело issue #478 целиком и все 3 комментария (аналитика, занятие, передача на ревью, решения владельца от 06.09.2026). 3. Прочитан ТЗ целиком (531 строка, разделы 1–20). 4. Прочитаны канонические документы, которые ТЗ обязуется обновить при реализации: `docs/CANVAS.md`, `docs/WALL-THICKNESS.md`, `docs/CONFIG-COMPATIBILITY.md`, `docs/ARCHITECTURE.md`, `docs/USER-GUIDE.ru.md` — сверена терминология («черновик», «независимая стена», resume-жест) с тем, что уже описано там сегодня. 5. Технические утверждения ТЗ и исходного текста issue сверены с текущим кодом на `origin/dev` (`fc5973c2`), а не приняты на слово: - `MAX_ROOM_DRAFTS=200`, `MAX_DRAFT_SEGMENTS=2000`, `MAX_PARTITIONS=2000` — существуют в `src/houseplan-card.ts`, `src/houseplan-editor-runtime.ts`, `custom_components/houseplan/validation.py` ровно как описано; - `_offerWallFaces`, `_offerExistingWallFace`, `_wallGraphSources`, `_planStructuralGeometrySnapshot` — существуют в `src/houseplan-editor-runtime.ts` с сигнатурами, соответствующими описанию в issue; - `fixedTopologyWallLineageHints` — существует в `src/wall-segment-model.ts` и используется в `houseplan-editor-runtime.ts`; - `partitionsReconciled` / `redundantDraftsRemoved` — существуют в `src/plan-optimizer.ts` и `src/coincident-partitions.ts` ровно с той семантикой, которую ТЗ предлагает свести к нулю; - `draft-live-preflight.ts` / `draft-live-commit.ts` (Fast path #461) — существуют, есть тест `test/draft-live-preflight.test.mjs`; - `WALL_SEGMENT_MODEL_VERSION` и `PLAN_MODEL_VERSION` действительно равны `9` сегодня и исторически бампались синхронно (`git log -p` по обеим константам) — заявленный переход v9→v10 не расходится с прецедентом; - `wall_model_client_outdated` — существующий код ошибки (`src/houseplan-card.ts`, `src/space-copy-runtime.ts`, все локали), а не изобретённый в ТЗ; - `mutation-gate` как механизм (AC13 требует «мутант... краснит gate») — реально существующая инфраструктура (`scripts/mutation-gate.mjs`, `test/mutation-gate.test.mjs`, `docs/TESTING.md`), не выдуманный инструмент. 6. Сверена таблица `docs/specs/README.md`: новая строка вставлена без колонки «Статус ТЗ» — соответствует зачистке из PROCESS.md §7.3 п.1, уже принятой в остальной таблице. 7. Проверено соответствие §7.1 (обязательные разделы) и формату AC (каждый пункт называет способ доказательства). Гейты (typecheck/test/build) не гонялись: на этом этапе (spec) продуктового кода в ветке нет, гонять их не над чем. Это не пропуск гейта, а его неприменимость к диапазону изменений раунда. ## Разбор по требованиям процесса ### §7.1 — обязательные разделы Все присутствуют, хотя «контракт поведения» не оформлен единым заголовком, а распределён по §8–12 (активная цепочка / face detection / Undo-Redo / limits / import-export) — это не нарушение, содержание есть и логично сгруппировано: сценарий (§1) · что человек увидит (§2) · проблема (§3) · скоуп/не-скоуп (§5) · контракт поведения (§8–12) · UX/i18n (§13) · модель данных и миграция (§6–7) · критерии приёмки AC1–AC15, каждый со способом доказательства (§14) · план автотестов (§15) · риски (§17) · откат (§18) · release-артефакты (§19). Два продуктовых раздела («сценарий», «что человек увидит») идут первыми, как требует PROCESS.md, и оба не используют термины реализации. ### Продуктовые вопросы vs технические Открытых продуктовых вопросов нет: владелец 06.09.2026 ответил ровно на два продуктовых вопроса (жест возобновления контура; подсветка незамкнутых стен), и оба ответа перенесены в §4 ТЗ дословно. Раздел 20 «Принятые технические предположения» содержит только техническое: имя/расположение session-состояния, переименование файлов fast-path, источник fallback ID, место экстракции reconciliation-логики, судьба deprecated-счётчика, механизм CSS-подсветки. Ни один из этих семи пунктов не задевает то, что видит пользователь — деление между продуктовым и техническим сделано верно, ничего техническое не выдано владельцу, и ничего продуктовое не спрятано в «технические предположения». ### Догадка, выданная за факт Не найдена. Каждое нетривиальное техническое утверждение («уже сегодня предложение комнаты работает и на чужих партициях», «дубли схлопывает только оптимизатор», названные функции и константы) проверено против кода и подтвердилось — см. предыдущий раздел. Формулировки вида «предполагаю» отсутствуют там, где нужно было бы решение, потому что владелец их уже принял. ### Однозначность AC 15 AC, каждый называет способ доказательства (`unit`, `unit + Python parity`, `smoke`, `performance + mutation`, `golden verify`, `typecheck+unit+build+docs review`). Формулировки конкретны и допускают проверку «упало/не упало»: например AC7 требует «итог содержит одну masonry, нет coincident partitions» — проверяемо инвариантом, а не оценочно. AC13 явно требует существования красного мутанта на «coincident partition либо draft» — это ровно дисциплина «тест должен уметь падать», перенесённая в план ещё до кода. ### Миграция и откат Миграция v9→v10 нормативно описана (§7.1–7.3): один partition на одно legacy edge, без слияния/дедупликации, детерминированные ID с указанным источником энтропии, требование TS/Python parity. Откат (§18) корректно разделяет кодовый revert (безопасен) и уже сохранённый v10-конфиг (несовместим со старой карточкой по замыслу, downgrade запрещён явно) — это соответствует `docs/CONFIG-COMPATIBILITY.md`, где подобные необратимые migrate-on-write переходы уже прецедент. ### Соответствие docs/SCOPE.md Задача — tech debt/миграция модели, устраняющая дублирующую сущность и невидимо накапливающийся артефакт (`room_drafts`, не отличимый от обычной стены после reload, блокирующий resize соседней стены). Это поддерживает J6 («Keep the plan true as the home evolves») — упрощает и делает надёжнее механизм разметки контура/комнаты, не расширяя видимый функционал за пределы уже существующего поведения. Новых кнопок, настроек или диалогов ТЗ явно не вводит (§5.2, §13) — соответствует принципу «View mode остаётся продуктом», редакторская сложность не растёт для пользователя. ### Release-артефакты и User-Visible `User-Visible: yes` обоснован верно (§19): исчезает resume персистентного черновика, меняется видимое поведение незамкнутых стен после reload. Список документов для обновления полный и включает оба changelog, что при реализации обязано попасть в тот же коммит по AGENTS.md. ## Что проверено и корректно - Обязательные разделы §7.1 — все на месте. - Каждый AC — с методом доказательства, формулировки допускают падение теста. - Технические утверждения ТЗ и предпосылки issue — верны по факту чтения кода `origin/dev`, догадок, выданных за решённый факт, не найдено. - Продуктовые решения владельца зафиксированы дословно и исчерпывающе; раздел «технические предположения» не содержит скрытых продуктовых развилок. - Скоуп/не-скоуп конкретны и проверяемы, границы задачи не размыты. - Миграция и откат внутренне согласованы и соответствуют прецеденту `docs/CONFIG-COMPATIBILITY.md`. - `docs/specs/README.md` обновлён в формате, уже принятом для остальной таблицы (без колонки статуса). ## Чего не проверял - Не гонял `npx tsc --noEmit`/`npm test`/`npm run build` — в ветке нет продуктового кода, гонять их не над чем на этапе spec. - Не проверял `npm run invariants` — геометрия ещё не реализована, инварианты модели проверять не на чем. - Не читал полностью весь текст `docs/CANVAS.md`/`docs/WALL-THICKNESS.md`/ `docs/ARCHITECTURE.md` построчно — выборочно сверил только фрагменты, относящиеся к `room_drafts`/lineage/reconciliation, поскольку остальное не затрагивается этим ТЗ. - Не оценивал реализуемость помодульно (файлы/хелперы) — ТЗ прямо и верно относит это к техническим решениям вне продуктового ревью (§20, PROCESS.md §7.1 «всё, чего пользователь не наблюдает, агенты решают сами»). - Не связывался с #477/#461/#173/#282/#314 по существу — они упомянуты как контекст связи, не как обязательства этого ТЗ; не-скоуп (§5.2) явно исключает пересечение с #461. ## Находки Нет ни одной находки уровня High или Medium. Low-находок, требующих правки или пометки waived, тоже нет. ## Итог ТЗ полное, однозначное, все обязательные разделы на месте, каждый AC проверяем и называет способ доказательства, продуктовых вопросов не осталось, а технические утверждения подтверждены чтением актуального кода, а не приняты на веру. Задача полностью укладывается в `docs/SCOPE.md` (J6) и не расширяет скоуп за счёт незаявленной пользователю функциональности. **Вердикт: зелёный · заход r1 · блокирующих циклов 0/4 · High: 0 · Medium: 0** --- ## Материал раунда - Ветка: `issue/478-remove-room-drafts`, коммит `30a16ed36461` — ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала: `66116fde28ee91b0ee64d7dc0d3125b82c972505` ``` git log --all --format='%H %T' | grep 66116fde28ee ``` - ТЗ `docs/specs/478-remove-room-drafts.md`, блоб `8817d2c4307eed78b4eba9f731b3b6fd7c5270c3` ``` git log --all --find-object=8817d2c4307eed78b4eba9f731b3b6fd7c5270c3 -- docs/specs/478-remove-room-drafts.md ```