Files
houseplan-card/docs/reviews/SPEC-REVIEW-478-r1.md
T
2026-09-06 17:37:09 +03:00

16 KiB
Raw Blame History

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