Files
houseplan-card/docs/reviews/SPEC-REVIEW-319-r1.md
T
2026-08-26 15:11:38 +00:00

120 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPEC-REVIEW-319-r1
Issue: #319 · Этап: spec (лёгкий трек `small`) · Заход r1 · блокирующих циклов 0/2
## Скоуп
Правка одной ветки условия в `validate_wall_model_transition`
(`custom_components/houseplan/validation.py:169-203`): гард «unchanged wall
catalogue» (строки 193-203) должен применяться только при `new_model <=
old_model`, а не безусловно при `old_model >= 8 and new_model >= 8`. Цель —
пропускать первую запись клиента v9 поверх v8-документа с осиротевшим
`open_span`/`open_to`, которую миграция #306 делает нередактируемой навсегда.
ТЗ — в теле issue (small-трек), файла в `docs/specs/` нет, что соответствует
§5 PROCESS.md.
## Как проверялось
Ревью полное (заход r1, дельты нет). Материал: тело issue #319 + комментарий
`S2-analysis` владельца (решение зафиксировано 2026-08-26), текущий код
`custom_components/houseplan/validation.py`, `src/wall-segment-model.ts`,
`src/plan-optimizer.ts`, существующие тесты
`tests_backend/test_wall_segment_model.py`, канон `docs/SCOPE.md`,
`docs/WALL-THICKNESS.md`.
1. Прочитан `validate_wall_model_transition` целиком (строки 169-203) —
условие и текст ошибки в коде дословно совпадают с тем, что описывает
issue («причина, доказана исполнением»). Ветка `old_model >= 8 and
new_model < 8` (легаси-клиент) действительно отдельная и правкой не
затрагивается.
2. Проверена посылка контракта «устаревший клиент не может поднять
`model_version`»: `WALL_SEGMENT_MODEL_VERSION = 9`
(`src/wall-segment-model.ts:21`) — константа кода, `commitWallSegmentModel`
всегда пишет `config.model_version = WALL_SEGMENT_MODEL_VERSION`
(`src/wall-segment-model.ts:724`), независимо от того, что было прочитано.
`plan-optimizer.ts` («Оптимизировать планы») использует ту же константу
(`PLAN_MODEL_VERSION = 9`, `src/plan-optimizer.ts:38`) и сам вызывает
`commitWallSegmentModelInPlace`. Значит клиент старой сборки (константа 8)
физически не может отправить `new_model=9` — посылка верна, это не догадка.
3. Проверено, не открывает ли ослабление гарда дыру в целостности данных:
независимая схемная проверка `_config_wall_segment_invariants`
(`validation.py:1766-1817`) при `model_version >= 8` жёстко сверяет
`room.poly` edge-by-edge с координатами `wall_segments`
(`_canonical_segment_key`, строка 1793-1795) и `space.walls` с
`wall_segments` (строка 1811-1817) — независимо от
`validate_wall_model_transition`. Реальное изменение геометрии контура без
соответствующего обновления каталога `wall_segments` отклоняется этой
схемной проверкой в любом случае, даже если гард транзита ослаблен. Гард
транзита ловит только специфический случай: `_catalog_coupled_wall_
geometry_projection` включает `open_spans`/`open_to`, поэтому просто
*удаление* легаси-поля миграцией меняет «проекцию контура», не трогая ни
`poly`, ни `wall_ids`, ни `wall_segments`. Спуфинг `model_version` наверх
не даёт обойти геометрическую проверку — вывод: ослабление гарда не
создаёт находки Medium/High по целостности данных.
4. Сверены AC1-AC4 с существующей тестовой инфраструктурой
`tests_backend/test_wall_segment_model.py`: фикстура
`test/fixtures/282-wall-identity-parity.json` существует и уже используется
(строка 36-46); паттерн «stored/previous → submitted/candidate →
`validate_wall_model_transition`» уже используется в 4 существующих
тестах (строки 184-298), включая ровно тот сценарий AC2/#314
(`test_current_wall_model_independent_geometry_does_not_require_contour_
catalog_change`, строка 226). AC1/AC2/AC3/AC4 доказуемы предложенным
способом без дополнительных догадок об инфраструктуре.
5. Проверено, что `test_stale_client_echoing_v8_catalog_gets_the_named_error`
(строка 210-223, `old_model=9, new_model=9`) и
`test_stale_client_round_trip_is_hydrated_but_structural_change_is_rejected`
(строка 184-207, `new_model<8` ветка) остаются зелёными под новым условием
`new_model <= old_model` — оба явно соответствуют AC3/AC4.
6. Проверен канон `docs/WALL-THICKNESS.md` — «canonical model v9 never writes
compatibility `open_spans` or `open_to`» (строка 76), противоречий с ТЗ
нет.
7. `docs/SCOPE.md`: это баг-фикс, восстанавливающий J6 («Keep the plan true
as the home evolves» — drag/resize, merge/split), а не новая функция;
решение владельца по направлению фикса уже зафиксировано в
`S2-analysis`-комментарии 2026-08-26. Открытых продуктовых вопросов нет.
## Находки
Нет. Ни одной High/Medium/Low.
## Что проверено и корректно
- Соответствие §5 (лёгкий трек): сложность/риск, одна поверхность
(`validation.py`), нет миграции конфига/новых полей/UX-контракта, нет
влияния на touch/perf — подтверждено чтением кода, а не только заявлением
автора оценки.
- Структура ТЗ в теле issue соответствует упрощённому шаблону §5: проблема ·
контракт · AC1…AC4 с доказательством · откат — все разделы на месте.
- Контракт («гард применяется только при `new_model <= old_model`») —
однозначная булева формулировка, прямо переносимая в код без дополнительной
интерпретации; не риторика, а точное условие.
- Посылка «устаревший клиент не может поднять `model_version`» проверена по
фронтенд-коду, а не принята на веру.
- Отсутствие дыры в целостности данных при ослаблении гарда проверено по
независимой схемной проверке `_config_wall_segment_invariants`.
- Каждый AC указывает способ доказательства (регрессионный тест, зелёные
существующие тесты) и воспроизводим по существующей тестовой
инфраструктуре проекта.
- Откат: ревертом одного коммита, правка не трогает данные (только
валидацию записи) — корректно и достаточно для этого класса изменений.
- Ветка `old_model >= 8 and new_model < 8` (легаси-клиент, AC4) явно не
меняется — подтверждено чтением кода.
## Чего не проверял
- Не запускал `pytest tests_backend` — на этапе spec кода фикса ещё нет,
запускать нечего; это часть код-ревью следующего этапа.
- Не проверял вручную UI/тост «Обновите карточку» в браузере — это
поведенческое доказательство, а не спецификационное; относится к
код-ревью/смокам.
- Не выяснял в глубину, обрабатывает ли `plan-optimizer.ts` путь отдельно от
обычной структурной записи в бэкенде — не требуется: бэкенд-валидация одна
на все пути записи, и AC1 формулирует «любая структурная запись», что
корректно покрывает и «Оптимизировать планы» без отдельного AC.
## Вердикт
Зелёный. ТЗ технически обосновано, контракт однозначен и реализуем буквально,
все AC проверяемы предложенным способом, открытых продуктовых вопросов нет,
скоуп совпадает с job J6 из `docs/SCOPE.md`. Готово к разработке.