16 KiB
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.
Как проверялось
- Прочитан
docs/SCOPE.md,AGENTS.md,PROCESS.md§2.4/§7.1/§4/§7.2/§12. - Прочитано тело issue #478 целиком и все 3 комментария (аналитика, занятие, передача на ревью, решения владельца от 06.09.2026).
- Прочитан ТЗ целиком (531 строка, разделы 1–20).
- Прочитаны канонические документы, которые ТЗ обязуется обновить при
реализации:
docs/CANVAS.md,docs/WALL-THICKNESS.md,docs/CONFIG-COMPATIBILITY.md,docs/ARCHITECTURE.md,docs/USER-GUIDE.ru.md— сверена терминология («черновик», «независимая стена», resume-жест) с тем, что уже описано там сегодня. - Технические утверждения ТЗ и исходного текста 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), не выдуманный инструмент.
- Сверена таблица
docs/specs/README.md: новая строка вставлена без колонки «Статус ТЗ» — соответствует зачистке из PROCESS.md §7.3 п.1, уже принятой в остальной таблице. - Проверено соответствие §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— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
66116fde28ee91b0ee64d7dc0d3125b82c972505git log --all --format='%H %T' | grep 66116fde28ee - ТЗ
docs/specs/478-remove-room-drafts.md, блоб8817d2c4307eed78b4eba9f731b3b6fd7c5270c3git log --all --find-object=8817d2c4307eed78b4eba9f731b3b6fd7c5270c3 -- docs/specs/478-remove-room-drafts.md