mirror of
https://github.com/Matysh/houseplan-card
synced 2026-09-30 19:58:50 +00:00
521 lines
32 KiB
Markdown
521 lines
32 KiB
Markdown
# Issue #132 — проёмы в независимых стенах
|
||
|
||
- **Issue:** https://github.com/Matysh/houseplan-card/issues/132
|
||
- **Связанный bug в том же scope:** https://github.com/Matysh/houseplan-card/issues/185
|
||
- **Статус документа:** актуализировано после #173 и #157; готово к ревью ТЗ
|
||
- **Приоритет:** P2
|
||
- **Тип:** feature, обычный трек
|
||
- **Пользовательское изменение:** да
|
||
|
||
## 1. Сценарий
|
||
|
||
Администратор рисует незамкнутую цепочку инструментом «Стены», завершает её
|
||
сменой инструмента, редактора или этажа и размещает в одном сохранённом сегменте
|
||
дверь, окно, ворота либо открытый проём тем же инструментом «Проём», которым
|
||
работает со стенами комнат. Домочадцы затем видят корректный разрыв, а для
|
||
stateful-типов — тот же live state и действия в View/kiosk.
|
||
|
||
## 2. Что человек увидит до и после
|
||
|
||
До изменения завершённая независимая стена остаётся сплошной и не принимает
|
||
проём; после изменения выбранные дверь, окно, ворота или открытый проём вырезают
|
||
её тело, перемещаются вместе с выбранным отрезком стены и ведут себя как тот же
|
||
тип проёма в обычной стене. При этом стена с проёмом по #185 остаётся полноценным
|
||
ребром для распознавания замкнутой комнаты.
|
||
|
||
## 3. Проблема и связь со scope
|
||
|
||
`partitions[]` — канонические независимые физические стены, но current opening
|
||
placement/index принимает только derived room walls. Physical union специально
|
||
добавляет partitions после opening cuts, поэтому проём не может вырезать их.
|
||
|
||
#173 заменил отдельный публичный инструмент «Перегородка» одной непрерывной
|
||
цепочкой «Стены». Активные сегменты crash-safe живут в `room_drafts`; явное
|
||
завершение превращает каждый сегмент в отдельный `partition` со stable `id`.
|
||
Face graph использует сохранённые partitions и может создать room wall на той же
|
||
оси, не удаляя исходный объект. Это делает composite room-wall/partition overlap
|
||
штатным, а не ошибочным случаем.
|
||
|
||
#185 фиксирует более новый продуктовый контракт: проём является свойством стены,
|
||
а не разрывом её структурного ребра. Поэтому opening cuts влияют на физическую и
|
||
световую геометрию, но room-face detection должен уметь замыкать область через
|
||
room wall или partition с любым поддержанным проёмом. `open_spans` остаются
|
||
реальным отсутствием стены и в topology не возвращаются.
|
||
|
||
Функция закрывает J4: администратор может без внешнего SVG точно воспроизвести
|
||
план. Правильный итоговый View также поддерживает J1/J2/J3: физическая геометрия,
|
||
contact/lock status и безопасные actions не расходятся с домом.
|
||
|
||
## 4. Решения владельца
|
||
|
||
1. На независимой стене разрешены все существующие типы: `door`, `window`,
|
||
`gate`, `passage`. #132 не добавляет новый opening type.
|
||
2. Проём следует за host при движении перегородки.
|
||
3. Удаление перегородки с проёмами требует confirmation со списком и удаляет их
|
||
только после явного согласия.
|
||
4. Contact, lock, badges и actions идентичны одноимённому проёму в room wall;
|
||
меняется только host geometry.
|
||
5. Физический passage в перегородке участвует в световой геометрии по тем же
|
||
type-specific правилам, что passage в стене.
|
||
6. При точном совпадении saved partition со стеной комнаты они считаются одним
|
||
составным физическим барьером без дополнительного host chooser. Explicit host
|
||
остаётся partition; hosted interval прорезает оба совпадающих тела. После
|
||
движения partition вычисляемый room-wall cut исчезает, room config не
|
||
переписывается.
|
||
7. По #185 проём любого поддержанного типа не разрывает структурное ребро для
|
||
room-face detection. Контур через стену с проёмом замыкается, а проём и его
|
||
host сохраняются.
|
||
|
||
## 5. Scope
|
||
|
||
- явная persisted host identity для partition opening;
|
||
- placement/hover/drag/edit/delete door, window, gate и passage на partition;
|
||
- full-depth cut, jamb/tunnel/symbol geometry с толщиной partition;
|
||
- composite cut совпадающих partition + derived room wall без persisted room
|
||
provenance;
|
||
- движение/удаление host и единая Undo/Redo команда;
|
||
- structural wall axes для #185 отдельно от physical opening gaps; regression
|
||
контракта #173 для endpoint/T/X/collinear faces и реальных `open_spans`;
|
||
- Flat Plan/View/kiosk/static и hidden Isometric renderers;
|
||
- clean floor, Glow barrier/source guard и действующие sun semantics;
|
||
- existing opening HA state/actions/security;
|
||
- backend validation, import/export/optimization compatibility;
|
||
- i18n, user/canonical docs, unit/backend/smoke/golden/performance evidence.
|
||
|
||
## 6. Не входит в задачу
|
||
|
||
- новый opening type или новая семантика существующего `passage`;
|
||
- проёмы в unfinished room drafts или columns;
|
||
- несколько host segments на один opening;
|
||
- автоматическая конверсия старого room-wall opening в partition opening;
|
||
- новый способ завершения Walls chain либо auto-resume finished partition;
|
||
- новая light model либо изменение window/door/gate semantics;
|
||
- sun rays, источником которых становится внутреннее partition window;
|
||
- полная touch parity Plan editor;
|
||
- свободное удаление host с неявной потерей его openings.
|
||
|
||
## 7. Модель данных и host identity
|
||
|
||
`OpeningCfg` получает optional host discriminator:
|
||
|
||
```ts
|
||
host?: {
|
||
kind: 'partition';
|
||
id: string;
|
||
t: number;
|
||
}
|
||
```
|
||
|
||
- `host` отсутствует — current legacy/current room-wall association по `x/y/
|
||
angle/length`; старые конфиги не мигрируют.
|
||
- `kind='partition'` — `id` обязан ссылаться на partition того же space.
|
||
- Один finished Walls segment создаёт один partition id. Бывшая multi-segment
|
||
chain не становится составным host: соседние segments двигаются и удаляются
|
||
независимо.
|
||
- `t` — нормализованное положение центра вдоль направленного `a→b`, `[0,1]`.
|
||
- `x`, `y`, `angle` остаются materialized compatibility projection и обновляются
|
||
атомарно из host; новый frontend считает valid host+t authority.
|
||
- `length` остаётся normalized physical length и существующий dialog показывает
|
||
cm/in по `cell_cm`.
|
||
|
||
Если implementation выбирает flat fields вместо object, semantics и
|
||
compatibility остаются теми же. Host kind расширяем discriminated union, но #132
|
||
добавляет только `partition`.
|
||
|
||
Backend new writes проверяет: existing partition id, finite `t`, finite geometry,
|
||
length fits host с jamb safety margin и total limits. Referentially invalid full
|
||
config write отклоняется, чтобы старый client не мог тихо удалить host и оставить
|
||
opening.
|
||
|
||
## 8. Canonical resolved opening
|
||
|
||
Один pure resolver строит immutable `ResolvedOpeningHost` для всех consumers:
|
||
|
||
- kind/id;
|
||
- directed centreline and normalized unit vector;
|
||
- center from `a + t(b-a)`;
|
||
- angle modulo current opening convention;
|
||
- full length and host depth from partition `cm`;
|
||
- derived room-wall intervals, collinear with and covering the hosted opening
|
||
interval within the canonical wall epsilon;
|
||
- validity/orphan reason;
|
||
- adjacent floor samples/type-specific passage policy.
|
||
|
||
Placement preview, symbol, body/composite cut, tunnel, hit test, light barriers,
|
||
Iso panel, move/delete и tests используют этот resolver. Запрещён render-only
|
||
nearest-wall fallback для explicit partition host. Coincident room-wall coverage
|
||
— вычисляемая projection valid partition host, а не второй persisted host.
|
||
|
||
Если endpoints хоста представлены в обратном порядке при canonical rewrite,
|
||
rewrite одновременно заменяет `t → 1-t`, сохраняя physical center и hinge/flip
|
||
orientation. Простое rigid translation сохраняет `t`, length и flip.
|
||
|
||
## 9. Placement UX
|
||
|
||
Активный инструмент «Проём» рассматривает два host kind:
|
||
|
||
- solid derived room-wall intervals;
|
||
- saved independent partition segments.
|
||
|
||
Hover preview показывает тот же translucent door/window/gate и привязывается к
|
||
partition axis; `passage` использует свою действующую symbol-less preview. Center
|
||
квантуется по действующему wall-bound grid step, length целиком помещается между
|
||
endpoints. Click повторно разрешает candidate и открывает существующий opening
|
||
dialog.
|
||
|
||
При близких candidates выбирается минимальная perpendicular distance, затем
|
||
полное покрытие opening length. Точное collinear overlap room-wall/partition,
|
||
покрывающее reserved interval, считается одним composite barrier и выбирает
|
||
explicit partition host без дополнительного dialog. Не совпадающая геометрически
|
||
tie остаётся неразрешимой и отклоняется с локализованной причиной. Existing
|
||
opening hit имеет приоритет над placement.
|
||
|
||
Toast `opening_no_wall` обновляется до «стены или перегородки». Virtual room span,
|
||
room draft и column остаются невалидными host. Touch placement — best effort;
|
||
pointercancel/multi-touch не сохраняют draft по safety floor.
|
||
|
||
## 10. Толщина, cut и symbol
|
||
|
||
Partition body строится из centreline и `cm`, half-depth с каждой стороны.
|
||
Hosted opening:
|
||
|
||
- вырезает body на полную глубину ровно по своему reserved interval;
|
||
- получает два flat jamb returns на концах cut;
|
||
- door swing начинается с выбранной face по `flip_v`;
|
||
- window glass располагается в середине partition depth;
|
||
- gate leaves используют current compact gate geometry;
|
||
- passage остаётся symbol-less full-depth cut по контракту #157;
|
||
- overlapping hosted openings не могут резервировать один interval дважды;
|
||
- junction patches после cut не должны заново закрыть passage.
|
||
|
||
Для zero/invalid thickness new partition невозможна по current validation
|
||
(1–100 cm). Malformed legacy host fail-dark: cut не применяется. Partition cut
|
||
выполняется по explicit host identity до единого physical union. Он дополнительно
|
||
вырезает только derived room-wall bodies, которые collinear и покрывают тот же
|
||
hosted interval в пределах canonical epsilon. Случайное пересечение, nearby
|
||
parallel wall или второй independent body не прорезаются.
|
||
|
||
## 11. Room topology и bug #185
|
||
|
||
Room-face graph получает два разных представления одной архитектуры:
|
||
|
||
- **physical/presentation:** opening interval вырезан из кладки и остаётся
|
||
проходом для изображения, hit geometry и type-specific света;
|
||
- **structural room topology:** axis валидной стены непрерывен через
|
||
door/window/gate/passage, потому что проём не отменяет принадлежность стены
|
||
контуру комнаты.
|
||
|
||
`buildPlanSnapGeometry()` либо следующий pure collector обязан сохранить
|
||
structural room/partition segment через opening interval для face traversal, не
|
||
возвращая этот interval в physical body. Действующий opening cut может оставаться
|
||
snap boundary/measure input, но не удаляет connectivity для room detection.
|
||
|
||
Контракт применяется одинаково к legacy room-wall opening и новому
|
||
partition-hosted opening. Контур, который последним segment опирается на такую
|
||
стену endpoint/T/X/collinear-примыканием, предлагает room по обычному #173 flow;
|
||
opening config, host, entities и materialized geometry не мутируют. `open_spans`
|
||
и отсутствующий/invalid host остаются настоящими gaps. Пассивные HA state ticks,
|
||
редактирование opening state и render не запускают room dialog.
|
||
|
||
## 12. Floor fill и tunnel
|
||
|
||
Partition не является границей room ownership. После вычитания hosted opening
|
||
видна уже существующая clean-floor/fill geometry под body. Дополнительный
|
||
нейтральный paper tunnel не рисуется.
|
||
|
||
Если partition пересекает две room fills, каждый side сохраняет свою canonical
|
||
fill ownership; cut не смешивает alpha и не создаёт новую room. Glow/sun layers
|
||
остаются выше base fill. Opening symbol скрывается по `hide_openings`, но physical
|
||
cut, HA state и barrier semantics остаются активны, как у room-wall opening.
|
||
|
||
## 13. Glow и light barriers
|
||
|
||
Решение владельца «проём пропускает свет» реализуется через type-specific
|
||
существующий контракт:
|
||
|
||
- door/gate/passage — transparent passage только там, где по обе стороны host
|
||
есть floor; `passage` не получает state или створку;
|
||
- exterior/no-floor с одной стороны остаётся opaque, чтобы Glow не уходил наружу;
|
||
- window сохраняет текущую политику: glass виден, но indoor Glow через него не
|
||
проходит;
|
||
- virtual boundary к partition не относится;
|
||
- source внутри valid interior door/gate/passage допустим; source внутри window
|
||
tunnel или invalid/orphan cut fail-dark.
|
||
|
||
Barrier geometry получает joined partition body **после вычитания только его
|
||
hosted passages**. Cache fingerprint включает host id/t, partition endpoints/cm,
|
||
opening length/type и geometry validity. HA-only state tick не rebuild-ит barriers.
|
||
|
||
## 14. Sun semantics
|
||
|
||
Partition window не является exterior room-boundary window и не создаёт новый
|
||
sun wedge. Existing exterior windows и their wedges сохраняются. Если canonical
|
||
sun/physical clipping использует independent body set, hosted cut не должен
|
||
восстанавливать opaque partition поверх уже разрешённого passage; никаких новых
|
||
direction/source rules #132 не вводит.
|
||
|
||
## 15. HA state, actions и security
|
||
|
||
Contact/invert, lock, badges, open amount, info card, lock/unlock confirmation и
|
||
`resolveHaBindingStatus()` не зависят от host kind.
|
||
|
||
- Door/gate lock action остаётся единственной sanctioned opening surface.
|
||
- Marker tombstone не отключает exact opening entity reference.
|
||
- Disabled/orphaned/unverified entity не выполняет service call.
|
||
- Window не получает lock control, если current type contract его не допускает.
|
||
- Passage остаётся inert: без contact/lock/invert/flip, badge, info card и service
|
||
call, по тому же контракту #157 на room wall.
|
||
- Перемещение host не меняет entity ids и runtime state.
|
||
|
||
Новый action resolver или отдельный partition status model запрещён.
|
||
|
||
## 16. Move, edit, delete и Undo
|
||
|
||
### Move/edit
|
||
|
||
Rigid drag одного partition segment обновляет endpoints и materialized
|
||
`x/y/angle` всех его hosted openings в одном preview/commit. `t`, length,
|
||
entities and flips сохраняются. Соседние segments бывшей Walls chain не
|
||
двигаются. Одна Undo/Redo command восстанавливает partition и все hosted
|
||
projections, включая computed composite cut.
|
||
|
||
Editor operation, которая сократила бы host меньше opening+jamb margin, блокирует
|
||
commit с причиной. Import/legacy invalid geometry не clamped молча.
|
||
|
||
### Delete
|
||
|
||
Если hosted openings нет, действует current delete path. Если есть:
|
||
|
||
1. dialog перечисляет openings в порядке `t`: локализованный type + length;
|
||
2. Cancel ничего не меняет;
|
||
3. Confirm атомарно удаляет partition и ровно его hosted openings;
|
||
4. одна Undo восстанавливает host и весь список с entities/geometry;
|
||
5. attachments отсутствуют у opening model, file cleanup не вызывается.
|
||
|
||
Удалять hosted openings без confirmation через generic cleanup запрещено.
|
||
|
||
## 17. Orphan и failure policy
|
||
|
||
Explicit partition opening с отсутствующим/invalid host:
|
||
|
||
- не вырезает physical body и не создаёт transparent light path;
|
||
- не рендерится как рабочий opening в View/kiosk/static/Iso;
|
||
- в Plan editor показывается диагностический orphan affordance в materialized
|
||
`x/y`, позволяющий удалить или перепривязать объект;
|
||
- entity action из orphan affordance не выполняется;
|
||
- production console не спамится; diagnostic reason доступна test/support hook.
|
||
|
||
Boolean failure по одному cut возвращает непрорезанный opaque partition
|
||
(fail-dark), не удаляет config и не делает все independent bodies прозрачными.
|
||
|
||
## 18. Migration и compatibility
|
||
|
||
- Старые openings без `host` читаются и пишутся по current room-wall contract.
|
||
- Existing room-wall opening около partition не начинает прорезать partition.
|
||
- Room-wall opening, существовавший до #132, сохраняет implicit host и участвует
|
||
в structural room topology по #185 без migration/write-on-read.
|
||
- New optional fields добавляются в frontend/backend schema и
|
||
`docs/CONFIG-COMPATIBILITY.md`/field registry.
|
||
- Export/import/backup сохраняют host object и referential order; counts остаются
|
||
в общем числе openings.
|
||
- Align/optimize переносит partition и hosted opening согласованно, не re-snaps
|
||
explicit host к nearby room wall.
|
||
- Older frontend may preserve unknown host fields, but backend referential
|
||
validation prevents destructive orphan write; backward visual support не
|
||
обещается до версии, реализующей #132.
|
||
|
||
Schema migration существующих данных не нужна: capability появляется только у
|
||
новых/явно перепривязанных openings.
|
||
|
||
## 19. i18n и accessibility
|
||
|
||
Новые en/ru strings:
|
||
|
||
- `toast.opening_no_wall` — обновлённая guidance/error «стена или независимая
|
||
стена»;
|
||
- `opening.host_partition` — host kind «Независимая стена» в
|
||
properties/diagnostic;
|
||
- `confirm.delete_partition_openings_title`,
|
||
`confirm.delete_partition_openings_body` и
|
||
`confirm.delete_partition_openings_item` — confirmation и строки списка с
|
||
параметрами `{count}`, `{type}`, `{length}`;
|
||
- `opening.partition_orphan` и `opening.rebind_partition` — orphan reason и
|
||
доступное действие перепривязки по §17.
|
||
|
||
Opening dialog, list и confirmation используют существующий `hp-dialog`, focus
|
||
trap, Escape и restore focus. List доступен screen reader; confirmation сообщает
|
||
точное количество. Canvas preview не является единственным объяснением host:
|
||
properties/diagnostic имеет текстовое accessible name.
|
||
|
||
**Touch editor: best effort / intentionally degraded.** Desktop остаётся
|
||
reference surface. На touch обязательны только safety floor: tap повторно решает
|
||
candidate на commit, а pinch/second pointer/pointercancel не создают opening, не
|
||
удаляют host и не завершают Walls chain. Hover parity не обещается; View и kiosk
|
||
не получают новых interactions.
|
||
|
||
## 20. Acceptance criteria
|
||
|
||
1. **AC1 — Walls workflow и placement.** После завершения multi-segment цепочки
|
||
«Стены» каждый saved segment является отдельным candidate host. На partition
|
||
размещаются door/window/gate/passage; active draft, column и virtual span не
|
||
принимаются. **Доказательство:** resolver/lifecycle unit + desktop smoke.
|
||
2. **AC2 — geometry и composite overlap.** Opening cut full-depth с корректными
|
||
jamb/symbol для 1/15/100 cm и diagonal partition. При точном покрывающем
|
||
room-wall overlap partition выбирается explicit host и весь composite barrier
|
||
прорезается; crossing/nearby/второй independent body не затрагиваются.
|
||
**Доказательство:** geometry units + reviewed golden.
|
||
3. **AC3 — host lifecycle.** Rigid move одного segment переносит его openings и
|
||
убирает computed room-wall cut; delete показывает точный список и атомарно
|
||
удаляет после Confirm; Undo/Redo восстанавливает host/openings/composite.
|
||
**Доказательство:** command units + browser smoke.
|
||
4. **AC4 — light.** Interior door/gate/passage пропускают Glow через полный
|
||
composite cut; exterior/no-floor варианты и window остаются opaque по current
|
||
policy; invalid host/boolean failure fail-dark. **Доказательство:** light
|
||
visibility units + smoke/golden.
|
||
5. **AC5 — #185 room closure.** Room-face detection считает wall axis
|
||
непрерывным через door/window/gate/passage как на legacy room wall, так и на
|
||
partition host. Endpoint/T/X/collinear closure предлагает room и сохраняет
|
||
opening bit-equivalent; `open_span` остаётся gap. **Доказательство:** pure
|
||
topology unit + production-bundle smoke, который краснеет на `origin/dev`.
|
||
6. **AC6 — no passive topology mutation.** HA state, opening edit/render,
|
||
reload и existing closed face не показывают room dialog и не меняют geometry;
|
||
только последний accepted Walls segment может инициировать предложение по
|
||
#173. **Доказательство:** wall-face graph unit + smoke.
|
||
7. **AC7 — render parity.** Plan/View/kiosk/static/Iso используют один resolved
|
||
host и не расходятся по center/angle/depth/open state; passage остаётся
|
||
symbol-less. **Доказательство:** cross-render smoke + golden.
|
||
8. **AC8 — HA/security parity.** Door/window/gate contact/lock/badge/actions
|
||
совпадают с room-wall contract, secure/disabled guards не меняются; passage
|
||
остаётся inert. **Доказательство:** unit matrix + smoke.
|
||
9. **AC9 — compatibility.** Legacy openings unchanged; host fields survive
|
||
save/export/import/optimize; invalid reference rejected or fail-dark; schema
|
||
принимает ровно четыре уже существующих type. **Доказательство:**
|
||
frontend/backend round-trip tests.
|
||
10. **AC10 — accessibility/touch safety.** Dialog/confirmation keyboard
|
||
доступны, touch cancel/multi-touch не создают opening и не завершают Walls
|
||
chain. **Доказательство:** browser smoke.
|
||
11. **AC11 — cache/performance.** HA tick и pointermove не rebuild-ят host,
|
||
composite или face topology; caches bounded и инвалидируются geometry host /
|
||
opening type/length. **Доказательство:** counters + performance smoke.
|
||
12. **AC12 — regression #173/#157.** Walls finish/no-auto-resume, room queue,
|
||
passage field restrictions, room-wall opening placement и планы без hosted
|
||
openings остаются совместимыми. **Доказательство:** existing targeted suites
|
||
+ named #132 smoke.
|
||
|
||
## 21. План автотестов
|
||
|
||
### Unit
|
||
|
||
- host serialization/resolution, t and reversed endpoints;
|
||
- room-wall vs partition candidate, composite/non-composite tie and length fit;
|
||
- horizontal/vertical/diagonal, 1/15/100 cm cut and jambs;
|
||
- overlap reservation, junction patch, coincident derived wall vs independent body;
|
||
- move/delete/Undo atomic snapshots;
|
||
- interior/exterior/window/passage light policy and fail-dark;
|
||
- structural-vs-physical collector: all four openings preserve room-face axes,
|
||
while `open_span` remains a gap; endpoint/T/X/collinear #185 matrix;
|
||
- only the latest accepted segment may cause room proposal; opening/HA updates do not;
|
||
- orphan, missing host and cache fingerprint;
|
||
- HA state/action host-kind parity.
|
||
|
||
### Backend
|
||
|
||
- host discriminator/id/t and referential validation;
|
||
- config size/count limits, export/import/unknown fields;
|
||
- canonical `passage` field restrictions from #157 with partition host present;
|
||
- malformed/missing partition rejection;
|
||
- full HA harness authority — Linux CI/WSL.
|
||
|
||
### Browser smoke
|
||
|
||
- finish multi-segment Walls chain; place/edit/drag door, window, gate and passage
|
||
on one resulting partition without resuming the chain;
|
||
- host move, delete Cancel/Confirm and Undo/Redo;
|
||
- contact opens, lock confirmation/action, hide_openings;
|
||
- Glow across interior passage and blocked by window/exterior passage;
|
||
- reproduce #185 on legacy room wall and hosted partition for all four types,
|
||
including T closure; verify `open_span` still blocks closure and opening config
|
||
is unchanged;
|
||
- exact composite overlap selects partition host without an extra chooser and
|
||
leaves no opaque wall layer across the opening;
|
||
- Plan/View/kiosk/static/Iso parity;
|
||
- touch cancel/pinch safety and keyboard confirmation;
|
||
- import/optimize round trip.
|
||
|
||
### Golden
|
||
|
||
- thick/diagonal partition with all four types, light/dark;
|
||
- coincident room-wall/partition composite before and after hosted cut;
|
||
- Glow before/after door passage and opaque window;
|
||
- hidden Iso panels/cuts and static-card parity;
|
||
- expected diffs accepted only from reviewed full Linux artifact.
|
||
|
||
### Performance
|
||
|
||
- cold structural build within current large-house budget;
|
||
- HA state tick geometry build count 0;
|
||
- repeated render cache growth 0/bounded caps;
|
||
- no per-opening repeated full wall index build.
|
||
|
||
## 22. Затронутые поверхности
|
||
|
||
- `OpeningCfg`, backend validation and config compatibility registry;
|
||
- opening placement/resolver, wall-thickness/physical geometry and command stack;
|
||
- `plan-snap-overlay`/wall-face structural collector and #173 room queue;
|
||
- Flat/static/Iso opening renderers;
|
||
- Glow/light and current sun physical consumers;
|
||
- Plan dialog/i18n, import/export/align/optimize;
|
||
- unit/backend/smoke/golden/performance fixtures;
|
||
- `docs/ARCHITECTURE.md`, `docs/CANVAS.md`, `docs/UX-MODES.md`,
|
||
`docs/WALL-THICKNESS.md`, `docs/LIGHT.md`, `docs/SUN.md`,
|
||
`docs/USER-GUIDE.ru.md`, `docs/TESTING.md`.
|
||
|
||
## 23. Риски и откат
|
||
|
||
| Риск | Мера |
|
||
| --- | --- |
|
||
| Host inference режет не ту стену | explicit partition id+t |
|
||
| Composite overlap режет nearby/crossing body | collinear full-interval coverage + negative units |
|
||
| Junction union закрывает passage | cut-aware physical union tests |
|
||
| #185 возвращает физическую кладку в проём | отдельные structural и physical projections |
|
||
| Opening начинает пассивно открывать room dialog | topology-delta и no-passive smoke #173 |
|
||
| Delete теряет entities | atomic command snapshot + confirmation |
|
||
| Light проходит через window/outside | existing type/floor-side policy |
|
||
| Older writer оставляет orphan | backend referential validation |
|
||
| Geometry cost растёт per opening | single immutable host/index snapshot |
|
||
|
||
Откат запрещает создание новых partition-host openings и возвращает body union
|
||
after room cuts. Уже сохранённые host objects должны либо оставаться
|
||
read-preserved/diagnostic, либо требовать downgrade warning; автоматически
|
||
преобразовывать их в room-wall openings нельзя.
|
||
|
||
## 24. Release-артефакты
|
||
|
||
Implementation commit имеет `User-Visible: yes` и одновременно обновляет:
|
||
|
||
- `docs/CHANGELOG.md` и `docs/CHANGELOG.ru.md`;
|
||
- `docs/USER-GUIDE.ru.md` — Walls finish, placement/types/move/delete/light и
|
||
closure через стену с проёмом;
|
||
- `docs/ARCHITECTURE.md`, `docs/CANVAS.md`, `docs/UX-MODES.md`,
|
||
`docs/WALL-THICKNESS.md`, `docs/LIGHT.md`, `docs/SUN.md`,
|
||
`docs/CONFIG-COMPATIBILITY.md`;
|
||
- `docs/TESTING.md`.
|
||
|
||
Нужны reviewed Flat/Iso/Glow golden artifacts, targeted browser/performance
|
||
reports and exact Linux backend evidence. Visual baselines принимаются только
|
||
через `golden:accept -- --reviewed` по полному Linux artifact.
|
||
|
||
## 25. Принятые технические предположения
|
||
|
||
- partition host хранится explicit id+t, materialized x/y/angle сохраняются;
|
||
- room-wall openings остаются implicit для backward compatibility;
|
||
- exact covering room-wall/partition overlap выбирает partition host как
|
||
owner-approved composite; прочие ambiguous ties отклоняются;
|
||
- structural room topology may use uncut wall axes, while all physical/light
|
||
consumers continue using type-specific cuts; shared persisted geometry не
|
||
создаётся;
|
||
- partition window не становится exterior sun source;
|
||
- invalid host/cut fail-dark and hidden from ordinary View;
|
||
- точная форма discriminator fields может меняться на ревью без изменения
|
||
persisted identity/lifecycle semantics.
|