diff --git a/docs/specs/316-opening-host-auto-resolution.md b/docs/specs/316-opening-host-auto-resolution.md index 8f763e51..36dccada 100644 --- a/docs/specs/316-opening-host-auto-resolution.md +++ b/docs/specs/316-opening-host-auto-resolution.md @@ -1,6 +1,6 @@ # Issue #316 — миграция v9 сама разрешает конфликт «проём ↔ нулевая стена» -Статус: ревизия 1. Родитель: #306 (стены нулевой толщины), контекст: #319 +Статус: ревизия 2 (закрывает M1/M2/Low ревью r1). Родитель: #306 (стены нулевой толщины), контекст: #319 (бэкенд-гард — отдельная задача). Решение владельца (чат, 2026-08-26): конфликт проёма при миграции разрешается автоматически, рисование нигде не блокируется; выпуск — v1.68.0-beta.4 вместе с #319. @@ -59,7 +59,7 @@ t·L + length/2]` пересекает интервал атома на обще Если кандидатов с `cm > 0` нет вовсе: берётся ближайший по расстоянию сегмент пространства, удовлетворяющий угловому критерию и вместимости, без ограничения допуска расстояния. Если и таких нет (в пространстве нет ни одного пригодного -сегмента) — проём сохраняется в данных **без `host`** и становится инертным: +сегмента) — проём сохраняется в данных **без `host`** и становится непривязанным (unhosted): не создаёт тоннель/вырез/тело, не участвует в физике, рендерится по своим `x/y` как прежде. `hostRoomOpenings` при initial migration пропускает такой проём вместо исключения; пост-v9 запись, не меняющая этот проём, обязана его @@ -81,10 +81,11 @@ t·L + length/2]` пересекает интервал атома на обще - **AC2 (3.1).** После миграции конфига B дверь остаётся на атоме с исходной толщиной; атомы span'а по обе стороны проёма — `cm: 0`. Доказательство: юнит-тест на `commitWallSegmentModel`. -- **AC3 (3.2).** Два коллинеарных совпадающих кандидата (контурный + - независимый, кейс #313) — host выбирается по правилу 3.2 и стабилен при - повторном прогоне (идемпотентность: второй прогон байт-идентичен). - Доказательство: юнит-тест. +- **AC3 (3.2).** Достижимая внутри `resolveRoomOpeningHost` неоднозначность: + центр проёма стоит на стыке двух контурных атомов одной стены на границе + смены толщины (оба атома проходят `eligible` — проём короткий и помещается в + каждом). Host выбирается по правилу 3.2 и стабилен при повторном прогоне + (идемпотентность: второй прогон байт-идентичен). Доказательство: юнит-тест. - **AC4 (3.3).** Проём вдали от всех стен мигрирует в состояние «без host», конфиг проходит `CONFIG_SCHEMA` бэкенда, повторная запись его сохраняет. Доказательство: юнит-тест фронта + бэкенд-тест схемы. @@ -94,16 +95,73 @@ t·L + length/2]` пересекает интервал атома на обще - **AC6.** Прогон миграции идемпотентен: `commit(commit(x)) == commit(x)` байтово на фикстурах AC1–AC4. Доказательство: юнит-тест. -## 5. Риски +## 5. Затронутые поверхности и артефакты + +### 5.1 Файлы + +- `src/wall-segment-model.ts` — `buildAtoms` (правило 3.1: легаси-кат не + зануляет атом, несущий проём), `hostRoomOpenings`/`resolveRoomOpeningHost` + (3.2/3.3 при `initialMigration`), пропуск непривязанных проёмов. +- `src/houseplan-card.ts` — без новых веток: barrier остаётся; проверить, что + непривязанный проём не ломает существующие писатели. +- `src/space-render.ts` — подтверждено ревью r1: рендер по `x/y` уже работает + для проёмов без host; правок не ожидается (если потребуются — в скоупе). +- `scripts/config-field-registry.mjs`, запись `spaces[].openings[].host=wall`: + `default` и `migration` больше не могут звучать как «required for contour + openings» / «materialize only when exactly one carrier is proven» — + формулировки обновляются на «materialized by migration §3 of #316; a + contour opening may persist unhosted after degraded migration», в том же + коммите, что и код (правило 11). +- `docs/CONFIG-COMPATIBILITY.md` — новый подраздел о непривязанном контурном + проёме как валидном v9-состоянии после деградированной миграции (§3.3): + не участвует в телах/вырезах, рендерится по `x/y`, переставляется picker'ом; + прежний абзац «missing/invalid host … fail dark» уточняется ссылкой. +- `tests_backend` — кейс схемы: v9-документ с контурным проёмом без host + принимается на чтение и запись (AC4). +- `demo/smoke_*` — новый смок репродукции AC1. + +### 5.2 i18n + +Без изменений: ключи `toast.zero_wall_migration_blocked` и +`wall_model.reason.opening-host` остаются для пост-v9 fail-closed пути (§2); +новых строк не появляется. + +### 5.3 Release-артефакты + +- CHANGELOG RU+EN — в реализационном коммите (`User-Visible: yes`). +- Golden: правило 3.1 меняет видимое зонирование `cm:0` (атом под проёмом + остаётся телесным) — добавляется сцена «span поверх стены с дверью» в + golden-матрицу; существующие сцены не должны измениться (в них нет + комбинации span+проём) — verify обязан подтвердить. +- USER-GUIDE — без правок: пользовательского UI-контракта не меняется, + поведение «рисование не блокируется» — восстановление обещанного. + +### 5.4 Производительность и touch + +Perf: правило 3.1 добавляет к атомизации один проход по проёмам пространства +(O(atoms × openings) на миграцию, единожды на документ) — вне горячего пути +рендера; performance-smoke не затрагивается. Touch-контракта правки не +касаются (миграция — не жест). + +## 6. Принято предположительно (поменять свободно) + +- Порядок tie-break в 3.2: текущий host → расстояние → больший `cm` → + меньший `id`. +- Семантика деградации 3.3: ближайший пригодный сегмент без лимита допуска, и + лишь затем непривязанное состояние; альтернатива «сразу непривязанный без + поиска» тоже согласуется с решением владельца. +- Имя состояния 3.3 в документации: «непривязанный (unhosted)». + +## 7. Риски - Изменение зонирования cm:0 (3.1) сдвигает границы атомов относительно beta.3 — на документах, уже мигрировавших на beta.3, правило 3.1 не сработает (span'ы уже удалены, атомы уже cm:0): такие проёмы попадут в 3.2/3.3. Принято: beta.3 прожила меньше суток, владелец предупреждён. -- «Инертный» проём (3.3) — новое состояние данных; риск ограничен рендером по +- Непривязанный проём (3.3) — новое состояние данных; риск ограничен рендером по x/y и пропуском в физике; picker уже умеет переставлять проёмы. -## 6. Откат +## 8. Откат Реверт ветки; миграция снова кидает `opening-host` (поведение beta.3). Данные, уже мигрировавшие с авто-разрешением, остаются валидными v9-документами.