Волна 5 эпика #674, перенос ТЗ (класс C). Из 240 файлов `docs/specs/` в `legacy/specs/` уехали 219: на них не ссылается ни один живой файл (код, тесты, скрипты, workflow, документы вне архива и ревью). Остались 21 ТЗ — на которые ссылаются код, ADR, ISOMETRIC, SUN, RADAR, LIGHT (`docs/specs/067`), DECOR-EDITOR, support-relay, и те, на которые ссылаются они сами; README каталога объясняет, где искать остальное. Открытых issue с файлом ТЗ среди перенесённых нет. Относительные ссылки перенесённых файлов переписаны (`../X` → `../../docs/X`, соседние оставшиеся ТЗ → `../../docs/specs/…`) — все 26 резолвятся. Попутно: битая ссылка в `089-isometric-view-stage1.md:8` на удалённый `089-isometric-view.md` — теперь команда `git show` по истории. Строка в `legacy/README.md`. Issue: #682 User-Visible: no Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd
23 KiB
Issue #231 — декоративный слой виден поверх заливок комнат
- Issue: https://github.com/Matysh/houseplan-card/issues/231
- Связанные контракты:
docs/DECOR-EDITOR.md,docs/BACKDROP.md,docs/TOUCH-SUPPORT.md, #19, #55 - Тип: bug, обычный полный трек
- Приоритет: P1
- Пользовательское изменение: да
1. Сценарий и персона
Персона: домашний администратор, который добавляет линии, фигуры, подписи или мебель в редакторе подложки, а затем проверяет план в View; домочадец или гость видит тот же план в View либо kiosk.
Сценарий: декоративный объект целиком или частично расположен внутри комнаты с непрозрачной либо полупрозрачной заливкой. В редакторе подложки объект создан и остаётся в конфигурации, но текущий SVG-порядок рисует его до комнаты. Заливка комнаты поэтому полностью скрывает декор либо смешивает его цвет со своим.
2. Что человек увидит до и после
До исправления декоративная линия, фигура, подпись или мебель может исчезнуть, попав внутрь комнаты. После исправления весь декоративный слой одинаково виден снаружи и внутри комнат: он лежит поверх пола и продолженного в проёмы пола, но не перекрывает свет, стены, двери, окна, ворота и устройства.
3. Проблема и подтверждённая причина
В полном renderer карточки _renderDecorLayer() вызывается после backdrop, но
до рендера комнат. Позже в том же SVG рисуются:
- заливки и контуры комнат;
- hover-заливка;
- обычные тоннели проёмов;
- Glow-base комнат;
- Glow-base тоннелей;
- Glow, солнце, стены и символы проёмов.
SVG рисует более поздних соседей поверх ранних, поэтому заливка перекрывает декор. Сами объекты не удаляются: дефект ограничен композицией полного плана.
Статическая houseplan-space-card сейчас намеренно не рисует decor и не является
источником расхождения.
4. Нормативные источники и приоритет
При расхождении применяются:
- решения владельца в #231, включая принятое решение Q1;
- это ТЗ после зелёного SPEC-REVIEW;
- существующие контракты Glow, солнца, физических стен, проёмов и устройств;
docs/DECOR-EDITOR.md,docs/BACKDROP.mdиdocs/TOUCH-SUPPORT.md;- текущая реализация как compatibility baseline для явно не изменяемого поведения.
5. Цели
- Сделать decor видимым поверх любой эффективной заливки комнаты.
- Не разрывать декор на тоннелях дверей, окон, ворот и открытых проёмов.
- Сохранить свет, стены, символы проёмов и устройства выше decor.
- Сохранить hide/editor/data contracts без новой настройки и миграции.
- Защитить порядок структурным и визуальным тестом, который падает при возврате старого расположения слоя.
6. Scope
В задачу входят:
- перенос единственного вызова
_renderDecorLayer()в общем SVG полного плана; - единый порядок для линий, прямоугольников, эллипсов, текста, мебели и будущих типов, которые выводятся тем же renderer;
- обычный View, kiosk и три редактора, использующие общий stage;
- обычные room fills, hover fill, обычные тоннели, Glow-base комнат и тоннелей;
- сохранение Glow pools, солнца, стен, opening symbols, устройств и room labels выше decor;
- browser regression smoke, documented mutant и visual/golden evidence;
- актуализация канонического описания слоёв, testing docs и обоих changelog.
7. Не входит в задачу
- настройка или per-object флаг «под планом»;
- автоматическое изменение координат старого декора;
- изменение формы, размера, цвета, opacity, blend mode или hit area декора;
- изменение порядка backdrop либо room-shaped paper;
- изменение алгоритмов заливки, Glow, солнца, стен или проёмов;
- добавление decor в
houseplan-space-card, экспорт «только планировка» или другой статический renderer; - новый UI, i18n-строки, persisted fields или migration;
- отдельный порядок для скрытой изометрии: она продолжает использовать общий floor scene и не получает второго decor renderer;
- принятие golden baseline на Windows либо выпуск без команды владельца.
8. Нормативный порядок слоёв
8.1. Обязательная граница decor
В полном plan SVG порядок снизу вверх должен быть эквивалентен:
scene background
room-shaped paper
plan image / backdrop
grid
room fills and their simple contours
room hover fill
opening tunnels: data fill
Glow-base rooms
opening tunnels: Glow-base
DECOR
live Glow pools
sun rays
physical/virtual wall presentation and editor overlays
opening symbols: door / window / gate / passage
devices and room labels
Дополнительные editor-only или hover-outline слои могут сохранять своё текущее место, если не нарушают следующие инварианты:
- любой
[data-hp="decor"]следует в DOM после room fill, hover fill, обоих видов тоннелей и Glow-base; - он предшествует live Glow, sun, wall bodies и opening symbols;
- HTML-слои устройств и подписей комнат остаются выше SVG decor.
Тоннель считается продолжением пола в проём, а не символом проёма. Поэтому линия через порог не обрывается заливкой тоннеля; сама дверь, окно, ворота или открытый проём остаются выше линии.
8.2. Glow
Glow-base является тёмной основой пола и располагается под decor. Live Glow pool и солнечные лучи являются светом и располагаются над decor. Специальный blend, компенсация цвета или отдельная opacity для decor не добавляются: свет визуально воздействует на него тем же существующим способом, что и на прочее содержимое пола.
Если targeted fixture показывает иной фактический результат из-за CSS isolation/compositing, реализация не должна молча переносить decor поверх live Glow. Это расхождение возвращается владельцу как продуктовый вопрос.
8.3. Существующий декор
Решение владельца по Q1:
- все старые и новые decor-объекты получают один порядок;
- новый compatibility-флаг не создаётся;
- объект, ранее заходивший под комнату в расчёте на ошибочное перекрытие, после обновления может проявиться; пользователь при необходимости один раз правит его геометрию.
Открытие и сохранение плана без редактирования не переписывает decor и не материализует новые поля.
9. Hide, editor и interaction contract
hide_decor: trueпо-прежнему удаляет весь decor из View, kiosk, Plan editor и Devices editor;- Background editor показывает decor даже при
hide_decor: true, чтобы слой можно было редактировать; - в Background editor decor остаётся интерактивным и визуально непрозрачным, а архитектура сохраняет существующее de-emphasis;
- вне Background editor decor остаётся визуальным и инертным: он не получает новое действие и не перехватывает tap/click у комнаты или устройства;
- backdrop остаётся ниже комнат и decor и сохраняет текущую editor-opacity.
10. UX, accessibility и touch
Новых элементов управления, focus targets, hover-правил и жестов нет. View и kiosk на mouse, touch и pen используют один исправленный renderer. Target sizes, pointer modality, pan, pinch, room hover и device actions не меняются.
Touch editor остаётся best effort согласно docs/TOUCH-SUPPORT.md; safety floor
не затрагивается, потому что задача не меняет распознавание или запись жестов.
11. Данные, migration, privacy и i18n
Config, layout, backend schema, localStorage, import/export и сериализация не меняются. Миграция не нужна. Новых пользовательских строк и i18n-ключей нет. Fixture использует синтетические ids и не содержит пользовательских данных.
12. Архитектура и зоны изменений
Ожидаемая реализация сохраняет один renderer и один DOM-узел слоя:
existing space.decor[]
→ existing _renderDecorLayer()
→ the same .decorlayer group
→ moved once between Glow-base tunnels and live Glow
Ожидаемые зоны:
src/houseplan-card.ts— порядок вызова слоя и поясняющий invariant comment;demo/smoke_decor_layer_order.mjsлибо эквивалентный целевой smoke;- синтетическая fixture/golden scenario с реальным пересечением слоёв;
scripts/mutation-gate.mjsи registry test для documented mutant;docs/BACKDROP.md, при необходимостиdocs/DECOR-EDITOR.md;docs/TESTING.md,docs/CHANGELOG.md,docs/CHANGELOG.ru.md;- docs screenshot artifacts, которые обязательный
check-docsпризнает изменившимися после правкиsrc/**; - три generated bundle copies после build.
Нельзя создавать второй decor renderer, дублировать группу либо решать дефект
CSS z-index, который не определяет paint order внутри одного SVG.
13. Performance и security
- число decor nodes, render calls, SVG groups и вычислений на кадр не растёт;
- не добавляются observers, timers, event listeners, network calls, canvas или offscreen compositing;
- move layer не меняет config write, service-call и permission paths;
- interaction/pointer security не ослабляется;
- targeted performance sanity подтверждает отсутствие второго
.decorlayerи прежнее число[data-hp="decor"]на fixture.
14. Acceptance criteria
- AC1 — decor поверх room fill. На синтетическом плане с непрозрачной custom-заливкой линия, прямоугольник, эллипс, текст и мебель, пересекающие границу комнаты, остаются видимы и сохраняют заданные style values внутри и снаружи комнаты. Доказательство: browser smoke + reviewed golden с semantic pixel assertions; тест красный на коде до #231.
- AC2 — decor поверх hover и пола проёма. Decor следует в DOM и визуально лежит после room hover fill, обычного тоннеля, Glow-base комнаты и Glow-base тоннеля; hover-подсветка не тонирует decor, а линия через порог не разрывается. Доказательство: browser DOM-order + pixel smoke на active room-hover, data-fill и no-fill/Glow fixtures.
- AC3 — верхние слои сохранены. Live Glow и солнце следуют после decor; wall bodies и door/window/gate/passage symbols перекрывают decor в точках пересечения; устройства и room labels остаются выше. Доказательство: browser DOM-order/pixel assertions и существующие Glow/sun/opening smokes.
- AC4 — hide/editor/backdrop parity.
hide_decorскрывает слой вне Background editor, Background editor всё равно показывает и редактирует его, backdrop остаётся ниже, а другие editors не получают новых действий. Доказательство: targeted smoke + существующиеsmoke_hide_layers,smoke_decorиsmoke_backdrop. - AC5 — mutant доказывает тест. Documented mutant возвращает вызов decor до room fills; целевой smoke либо его unit guard краснеет по AC1/AC2, а не только по отсутствию узла. Доказательство: mutation registry check и целевой mutant run.
- AC6 — compatibility и surfaces. Persisted decor остаётся байт-в-байт прежним, новых полей/i18n нет; общий full renderer даёт одинаковый порядок в View/kiosk/editors и не создаёт второй слой в скрытой изометрии; статическая карточка по-прежнему не рисует decor. Доказательство: unit/source review, browser parity и existing static/isometric smokes.
- AC7 — performance/security. Число групп и объектов не увеличено, нет новых per-frame вычислений, listeners, service calls или pointer targets. Доказательство: DOM count, source review и targeted performance sanity.
- AC8 — release artifacts. Канонический порядок слоёв, testing contract,
оба changelog, docs screenshots и golden evidence актуальны. Доказательство:
docs diff,
check-docs, reviewed Linux golden artifact перед beta.
15. План автотестов
15.1. Цикл реализации и код-ревью
npx tsc --noEmit
npm test
npm run build
node scripts/check-docs.mjs --external
node scripts/mutation-gate.mjs --check
node demo/smoke_decor_layer_order.mjs
node demo/smoke_glow.mjs
Сверяются три копии bundle. Ревьюер дополнительно выбирает затронутые существующие smokes по изменённым функциям/полям; минимум проверяются контракты Glow blending, opening tunnels, hide layers, decor и sun. Полный browser-smoke набор остаётся предрелизным гейтом.
15.2. Обязательная fixture
Fixture содержит не декларации, а реальные пересечения:
- opaque custom room fill и все поддерживаемые decor kinds;
- толстую стену с тоннелем, через который проходит контрастная decor line;
- вторую комнату без data fill, получающую Glow-base;
- включённый источник live Glow;
- окно с видимым солнечным лучом;
- physical wall, opening symbol, device и room label над decor;
- активный room hover с существующей
.room-hover-fill-layer; целевой assert проверяетcompareDocumentPositionот неё к.decorlayerи raster sample на decor внутри подсвеченной комнаты; - режимы
hide_decorи Background editor.
Для контрольных координат браузер считывает raster pixels либо эквивалентный
визуальный результат. Один compareDocumentPosition без пиксельного/поведенческого
assert не считается доказательством AC1–AC3.
15.3. Golden
До beta полный Linux artifact содержит минимум:
- Light-сцену с opaque fill и decor, пересекающим комнату и проём;
- Dark/Glow-сцену с decor между Glow-base и live light;
- semantic assertions, краснеющие при возврате старого порядка.
Windows capture диагностический. Baseline принимает отдельный reviewed Linux-артефакт по процессу; реализация сама golden не принимает.
16. Риски и меры
| Риск | Последствие | Мера и доказательство |
|---|---|---|
| Decor поставить до второго Glow-base прохода | Линия снова разорвётся/потемнеет в тоннеле | DOM-order и два visual sample для room base и tunnel base |
| Decor поднять выше live Glow или солнца | Свет перестанет визуально воздействовать на подписи и фигуры | Явный верхний инвариант + Glow/sun smoke |
| Decor поднять выше стен или symbols | Линии перекроют архитектуру и дверь | Pixel intersections с wall body и opening symbol |
| Изменить hide/editor semantics | Скрытый слой появится в View либо станет нередактируемым | Existing hide/decor smoke + отдельная проверка режима |
| Golden содержит декор, но не пересекает заливку | Эталон останется зелёным при регрессии | Semantic sample строго внутри opaque room и mutant proof |
| Старый декор проявится там, где его намеренно прятали | Изменится вид существующего плана | Явно принято владельцем; без скрытой миграции и нового флага |
| Перестановка повлияет на скрытую 2.5D сцену | Decor окажется над объёмными стенами | Общий lower/upper invariant и existing isometric smoke без отдельного renderer |
17. Откат
Откат одного implementation commit возвращает прежний порядок и связанные test/
docs artifacts. Данные не меняются, обратная миграция не нужна. Golden baseline
возвращается только из reviewed Linux artifact. Если до beta обнаружено, что
новая граница неверна именно для live Glow, issue возвращается в S3-spec за
продуктовым решением, а не получает молчаливую перестановку.
18. Release-артефакты
В user-visible implementation commit одновременно обновляются:
docs/CHANGELOG.mdиdocs/CHANGELOG.ru.mdсо ссылкой на #231;docs/BACKDROP.mdи при необходимостиdocs/DECOR-EDITOR.md;docs/TESTING.mdс layer-order/mutant contract;- обязательные docs screenshots после
src/**diff; - browser fixture/smoke и golden scenario/semantic assertions.
Release note: декор из редактора подложки больше не исчезает внутри комнат и в проёмах; свет, стены, проёмы и устройства по-прежнему рисуются поверх него. Сначала beta, stable — только по отдельной команде владельца.
19. Принятые предположения
- Решение владельца по Q1 означает осознанное проявление любого старого decor, который прежде перекрывался заливкой; автоматической compatibility-эвристики нет.
- Glow-base относится к полу, live Glow и солнце — к свету; поэтому decor располагается между ними без специального blend.
- Simple room contour остаётся вместе с room fill ниже decor; физическое тело стены остаётся выше decor.
houseplan-space-cardсохраняет текущий контракт без decor.- Для всех decor kinds используется одна
.decorlayer; типоспецифичного порядка нет. - Техническое имя нового smoke/mutant можно поменять без изменения AC, если доказательства остаются эквивалентными.