mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-30 19:58:50 +00:00
105 lines
6.6 KiB
Markdown
105 lines
6.6 KiB
Markdown
# Issue #281 — честный Resize после outer-partition reconciliation
|
||
|
||
- Дата: 2026-08-24
|
||
- Тип: regression bug · приоритет P1
|
||
- Issue: [#281](https://github.com/Matysh/houseplan-card/issues/281)
|
||
- Ветка: `issue/281-resize-zero-range`
|
||
- Статус ТЗ: одобрено self-review по решению владельца
|
||
|
||
Связанные контракты: #276, #277, #278, #280;
|
||
`docs/RESIZE.md`, `docs/WALL-THICKNESS.md`, `docs/TESTING.md`.
|
||
|
||
## 1. Проблема
|
||
|
||
Eligibility beta.8 проверяет только исходный `d=0`, поэтому handle выглядит
|
||
активным, хотя обе соседние grid-позиции блокируются endpoint-touching
|
||
partitions и `clampSafeResize()` всегда возвращает ноль.
|
||
|
||
Resize не должен скрыто двигать или игнорировать independent walls. Перед ним
|
||
явный Optimize обязан канонизировать только доказанно лишние тела; всё
|
||
неоднозначное остаётся препятствием.
|
||
|
||
## 2. Уточнение принятого default после self-review
|
||
|
||
В аналитическом комментарии Q1 ошибочно утверждалось, что две outer partitions
|
||
целевого `44.json` не имеют openings. Фактически каждая содержит hosted window.
|
||
Буквальный вариант «только без openings» не исправляет заявленный сценарий.
|
||
|
||
Минимальная системная поправка в пользу принятого пользовательского результата:
|
||
exact outer duplicate допускается и с hosted openings, только если все они
|
||
полностью и без конфликтов материализуются как ordinary outer-room openings по
|
||
тем же identity/geometry/envelope доказательствам #276/#280. Backend требует
|
||
solid boundary одного либо двух rooms. Ordinary conflicting opening,
|
||
неразрешимый host или overlap блокирует весь candidate. Resize по-прежнему не
|
||
двигает и не игнорирует partition.
|
||
|
||
## 3. Optimize-контракт
|
||
|
||
Один reconciliation pass рассматривает solid atomic interval:
|
||
|
||
- `shared`: ровно два owner rooms, действующий #276 contract;
|
||
- `outer`: ровно один owner room;
|
||
- partition exact endpoint-to-endpoint, known fields only, centred envelope;
|
||
- нет overlapping second partition, draft или column;
|
||
- итоговая толщина `max(roomCm, partitionCm)`;
|
||
- every hosted opening полностью покрывается room interval, materializes без
|
||
потери полей и не пересекает другие openings;
|
||
- после rehost ordinary association имеет ровно тот же набор owner rooms.
|
||
|
||
Partial/composite/virtual/ambiguous/unknown/conflicting случаи byte-equivalent.
|
||
Apply/reload/repeated Optimize идемпотентны; Undo возвращает partition и hosts.
|
||
|
||
## 4. Resize-контракт
|
||
|
||
`SafeResizeOptions` содержит реальный grid step. После построения plan resolver
|
||
обязан проверить `-step` и `+step` тем же `validateSafeResize`, который
|
||
используют preview/commit.
|
||
|
||
- хотя бы один valid соседний node → handle enabled;
|
||
- оба invalid → handle disabled, pointer не захватывается;
|
||
- если соседний candidate блокирует immutable physical obstacle, причина
|
||
`duplicate-physical-wall`; иначе существующий deterministic reason либо
|
||
`invalid-geometry`;
|
||
- формулировка #277 «enabled + zero-only range» отменяется.
|
||
|
||
Clamp остаётся contiguous и не перепрыгивает препятствие.
|
||
|
||
## 5. Acceptance criteria
|
||
|
||
1. Privacy-min fixture с двумя rooms, целевой shared wall, тремя exact outer
|
||
partitions и hosted windows воспроизводит старый zero range.
|
||
2. Optimize удаляет только exact duplicates, materializes windows, сохраняет
|
||
envelope; backend принимает candidate и повторный Optimize no-op.
|
||
3. После Optimize целевой handle допускает минимум один шаг в обе стороны;
|
||
fixed topology, две rooms, thickness/openings и segment count сохраняются.
|
||
4. До Optimize zero-range handle disabled с `duplicate-physical-wall`, не
|
||
захватывает pointer и не пишет config/history.
|
||
5. Partial/longer/unknown/opening-conflict/second partition/draft/column
|
||
остаются; их handle не становится ложноположительным.
|
||
6. Полный `C:\Temp\44.json`: Optimize candidate проходит frontend/backend,
|
||
целевая shared wall имеет ненулевой диапазон; inventory не содержит enabled
|
||
handle без соседнего valid grid node.
|
||
7. Typecheck, unit, build, Optimize/Resize production smokes зелёные; Linux HA
|
||
test подтверждает outer rehost Apply/reload/Undo.
|
||
|
||
## 6. Совместимость, UX и performance
|
||
|
||
Schema/model version не меняются. Stored result — существующие room wall и
|
||
ordinary opening. Disabled handle уже использует доступный focus/tap reason и
|
||
не требует нового UI/i18n. Дополнительные две validations выполняются один раз
|
||
в memoized eligibility, не на каждом pointermove; Optimize pass остаётся
|
||
явным, линейным по atomic intervals и candidates.
|
||
|
||
## 7. Self-review ТЗ
|
||
|
||
- Обнаруженное противоречие с приватным input не замаскировано: hosted windows
|
||
включены только через доказанный rehost, а не через Resize bypass.
|
||
- Одна validation функция определяет eligibility, live clamp и commit.
|
||
- Negative matrix сохраняет partitions как hard stops, когда proof неполон.
|
||
- Exact lifecycle проверяет обе стороны границы: Optimize canonicalization и
|
||
честную Resize affordance.
|
||
|
||
Вердикт: **approved** с описанным минимальным уточнением Q1. Внешнее
|
||
spec-review пропущено по прямому решению владельца.
|
||
|