16 KiB
SPEC-REVIEW-456-r1
- Issue: https://github.com/Matysh/houseplan-card/issues/456
- ТЗ:
docs/specs/456-copy-space.md(611 строк) - Материал: тело issue #456 + все комментарии на момент ревью, ТЗ на SHA
197853c4d48bb0800df306f6e0d33eb18a23eb6e(веткаissue/456-copy-space, идентична текущемуdev/mainплюс один документ). - Этап: spec (PROCESS.md §2.4), заход r1, блокирующих циклов израсходовано 0/4.
- Трек: полный (в S2-analysis явно назван критерий, который задача не
проходит: новый UX-контракт и составная запись с предварительной
оптимизацией — не
small).
Скоуп проверки
Issue #456 просит кнопку «Копировать» в диалоге настроек пространства: копия стен (включая независимые перегородки), проёмов, колонн, декора, вида и подложки под новым именем, без комнат и без привязок к устройствам, с опциональной предварительной оптимизацией всего плана перед копированием.
ТЗ полного трека обязано пройти §7.1: сценарий, что человек увидит,
проблема/подтверждённое состояние, скоуп/не-скоуп, контракт поведения (UX +
данные), модель данных и миграция, i18n, AC1…ACn с доказательством, план
автотестов, риски, откат, release-артефакты — все разделы присутствуют
(docs/specs/456-copy-space.md §1–20).
Как проверялось
Ревью кода в этом заходе не требуется — диапазон origin/dev...HEAD содержит
только docs/specs/456-copy-space.md и одну строку в docs/specs/README.md
(проверено git diff --stat origin/dev...HEAD), то есть класс C. Гейты §8
(typecheck/test/build) к этому коммиту неприменимы: продуктовый код не
менялся, изменённых поверхностей src/**/custom_components/**/*.py нет.
Вместо гейтов — состязательная проверка каждого фактического утверждения ТЗ против реального состояния репозитория (не «выглядит правдоподобно», а конкретный grep/read):
| Утверждение ТЗ | Проверено | Результат |
|---|---|---|
wall_segments не могут существовать без 1–2 владельцев-комнат |
validation.py:1902-1903 |
подтверждено: raise vol.Invalid("wall segment must have one or two room owners") |
| ID геометрии уникальны в пространстве не только внутри своего массива | validation.py:1581-1604, _space_geometry_invariants |
подтверждено: один общий seen-set на rooms/openings/decor/room_drafts/partitions/wall_columns/wall_segments |
MAX_SPACES=50, MAX_PARTITIONS=2000, MAX_OPENINGS=500, MAX_DECOR=1000, MAX_WALL_COLUMNS=500 |
validation.py:1107-1120 |
подтверждено дословно |
MAX_JUNCTION_VALENCE=6 (лимит стыков, «8 вместо 6») |
junction_limits.py:35 |
подтверждено |
rooms: [] — валидный model-v9 space (нет vol.Length(min=...)) |
validation.py:1677 |
подтверждено |
Потолки ядра исчерпаны полностью: houseplan-card.ts 13659, houseplan-editor-runtime.ts 14323 |
test/core-file-budget.test.mjs:21-24, фактический wc -l файлов (13658/14322, мера теста — split('\n').length, на 1 больше wc -l) |
подтверждено: оба файла ровно на потолке, запас 0 |
validate_partition_opening_hosts, validate_junction_limits, validate_opening_passages, validate_wall_model_transition существуют |
validation.py:175,630,737, junction_limits.py |
подтверждено |
_unique_title, plan_refs, decor asset accounting |
import_export.py:906,1393, plans.py:249,299 |
подтверждено |
_checkOptimizeGeometry, _reportPreflightFailure, houseplan/plan/optimize, PARTITION_OPENING_HOST_SCHEMA, gs.align_all |
grep по src/** |
все символы существуют |
| Кнопка Delete сегодня — единственная в footer edit-режима, вне danger-группы места для Copy пока нет | houseplan-editor-runtime.ts:14110-14120, houseplan-onboarding-runtime.ts:889-897 |
подтверждено, оба места дословно совпадают с описанием ТЗ |
Layout-ключ подписи комнаты 'rl_' + room.id — плоский, без пространства (мотивация не копировать комнаты) |
houseplan-card.ts:12660,12688,12916 |
подтверждено |
src/space-dialog.ts, src/coincident-partitions.ts, src/plan-optimizer.ts существуют как заявлено |
ls |
подтверждено |
| «Сегодня повтор этажа делается только перерисовкой с нуля» — не забыт ли уже существующий экспорт/импорт «Текущее пространство» + «Только планировка» (#167)? | docs/USER-GUIDE.ru.md:1789-1810, docs/specs/167-plan-only-export.md |
проверено отдельно — расхождения с реальностью не нашёл, см. ниже |
Проверка на дубль: #167 «Только планировка»
Существующий экспорт Current space + Plan only действительно переносит
стены/проёмы/декор/подложку без устройств — на первый взгляд похоже на то, что
просит #456. Но этот механизм сохраняет комнаты (docs/specs/167-plan-only-export.md
§1–2: «комнаты, стены, проёмы, декор и фон остаются»), а модель v9 не разрешает
wall_segment без владеющей комнаты (см. таблицу выше) — то есть импортированную
копию нельзя превратить в «стены без комнат» существующими средствами: удаление
старых комнат потребовало бы предварительно решить ровно ту же задачу
преобразования в partitions, которую и решает #456. Сценарий #456
(«комнаты в новом этаже другие») этим путём сегодня не закрывается. Дублирования
не нашёл; различие можно было бы явно назвать в §3 ТЗ, но отсутствие этого
предложения не создаёт риска неоднозначности AC — не поднимаю как находку.
Не проверялось (осознанно, вне гейтов spec-review)
- Автотесты, mutation-тесты, browser smoke — кода ещё нет, впервые появятся в
S6-in-progress; спецификация лишь обязана назвать способ доказательства (проверено — назван для всех AC1–AC13). npx tsc --noEmit/npm test/npm run build/check-docs/model-invariants— не прогонялись: диапазон диффа не содержитsrc/**иcustom_components/**/*.py, толькоdocs/specs/**(класс C), гейты §8 к этому коммиту не применимы.- Golden/performance/backend harness — неприменимо на этапе spec.
Находки
Блокирующих (High) и находок Medium в скоупе или вне скоупа не обнаружено.
Low (снята с записью, не правится): §3 ТЗ («подтверждённое текущее состояние») не упоминает уже существующий экспорт «Текущее пространство → Только планировка» (#167) и не объясняет явно, почему он не закрывает сценарий. Разбор выше показывает, что реального дублирования нет (существующий путь сохраняет комнаты, а модель не разрешает стены без владельца, то есть не даёт «стены без комнат» без решения той же задачи преобразования). Автору не нужно возвращать ТЗ ради одной поясняющей фразы — снимаю находку решением ревьюера, содержательного риска для AC она не несёт.
Что проверено и корректно
- Все AC1–AC13 однозначны, у каждого назван способ доказательства (unit/integration/backend/browser smoke) и назван хотя бы один мутант, который должен покраснеть — раздел «Чем краснеет» в теле issue и «Доказательство» в ТЗ совпадают по существу.
- Технический анализ (
wall_segmentsневозможны без комнат →room_draftsломает лимит стыков →partitionsпроходит все четыре валидатора) — не декларация, а результат реального прогона схемы/валидаторов наdemo/fixtures/large-house.mjs; цифры (49 перегородок, 34 проёма, 8 vs лимит 6) сверены с кодом и совпадают. - Продуктовые вопросы Q1–Q4 (существующие перегородки источника, когда именно нужен второй confirmation, куда переходит пользователь после успеха, поведение при геометрическом долге после Optimize) заданы владельцу пачкой с предлагаемым default и явно решены им — открытых продуктовых вопросов не осталось (§7.1 требование выполнено).
- Раздел «Принятые предположения» (§21) отделяет технические/мелкие решения от продуктовых и явно помечен как свободно оспоримый ревьюером — ни одна догадка не выдана за факт без пометки.
- Не-скоуп (§5) корректно исключает перенос комнат/устройств/markers/vacuum routes, перенос между установками HA (уже закрыт #167), изменение самой логики Optimize и Copy в onboarding.
- Ограничение по потолку core-файлов (M2 аудита 04.09) учтено прямо и корректно: потолки исчерпаны полностью (подтверждено измерением), и ТЗ требует либо вынос эквивалентного объёма, либо отдельное решение владельца — не игнорирует блокирующее ограничение.
- Модель данных:
rooms:[]/wall_segments:[]как обязательные пустые массивы — валидный model v9 space, миграции и новых config keys нет (§12); совместимость со старой версией card/integration («пространство без комнат с partitions») корректно вытекает из существующей схемы. - i18n, performance, откат, release-артефакты — разделы присутствуют и содержательны, а не формальные заглушки.
Чего не проверял
- Реализуемость части «инъецируемая фабрика ID» и точную раскладку
src/space-copy.ts— это архитектурная заметка ТЗ (§13), не факт о текущем коде; она специально помечена как решаемая свободно на код-ревью. - Точные строки i18n-ключей — ТЗ называет категории строк, а не финальные
ключи; это осознанно оставлено на реализацию (см. §21 п.7, техническое
решение) и не блокирует AC13 (гейты
i18n/i18n-dead-keysпроверят факт наличия во всех языках, а не конкретное имя ключа). - Все побочные ветки backend-валидаторов (
validate_wall_model_transitionи др.) построчно — прочитаны сигнатуры и место вызова, не построчный разбор всей логики; для этапа spec этого достаточно, вопрос «работает ли» встанет на код-ревью, когда появится реализация.
Вердикт
Зелёный. ТЗ полное, все обязательные разделы §7.1 на месте, каждый AC однозначен и снабжён способом доказательства, продуктовые вопросы закрыты владельцем, технические утверждения проверены чтением кода и не разошлись с реальностью. High: 0. Medium: 0. Low: 1, снята решением ревьюера с записью выше.
Материал раунда
- SHA материала:
197853c4d48bb0800df306f6e0d33eb18a23eb6e - Дерево:
git show 197853c4:docs/specs/456-copy-space.md(611 строк) - Диапазон:
origin/dev...197853c4=docs/specs/456-copy-space.md,docs/specs/README.md(2 файла, класс C)
Материал раунда
- Ветка:
issue/456-copy-space, коммит197853c4d48b— ребейз его осиротит, и это нормально: ниже якоря, которые ребейз не меняет. - Дерево материала:
ec1f135705c26ee5bcd3081516ebaa9b75ba5e1dgit log --all --format='%H %T' | grep ec1f135705c2 - ТЗ
docs/specs/456-copy-space.md, блобa1def3eeefa15cac8b1bfe0542eb8d1ad0515fcfgit log --all --find-object=a1def3eeefa15cac8b1bfe0542eb8d1ad0515fcf -- docs/specs/456-copy-space.md