From d6ec494a1af2d7f2d37cc73d5468de8ba4e2b781 Mon Sep 17 00:00:00 2001 From: Sergey Matyunin Date: Sat, 15 Aug 2026 03:50:34 +0300 Subject: [PATCH] docs: specify isometric toggle performance Issue: #124 User-Visible: no --- .../124-isometric-view-toggle-performance.md | 254 ++++++++++++++++++ docs/specs/README.md | 1 + 2 files changed, 255 insertions(+) create mode 100644 docs/specs/124-isometric-view-toggle-performance.md diff --git a/docs/specs/124-isometric-view-toggle-performance.md b/docs/specs/124-isometric-view-toggle-performance.md new file mode 100644 index 00000000..011f7e84 --- /dev/null +++ b/docs/specs/124-isometric-view-toggle-performance.md @@ -0,0 +1,254 @@ +# Issue #124 — isometric view toggle выполняет Stage 1 performance budget + +- **Issue:** https://github.com/Matysh/houseplan-card/issues/124 +- **Статус документа:** готово к будущей реализации; issue остаётся на `S3-spec` +- **Приоритет:** P2 +- **Тип:** bug/performance, обычный трек +- **Пользовательское изменение:** нет; скрытый Labs UI и pixels сохраняются + +## 1. Симптом и доказательство + +В exact-SHA Full Performance для `v1.63.0-beta.1` (`3270e039…`) профиль +`large-house-isometric-v1` измерил: + +- `viewToggleMs.median`: 192.8 ms; +- pre-#89 baseline: 31.7 ms; +- limit: 131.7 ms (`base + 20% + 100 ms`); +- все семь candidate samples: 189.2–213.4 ms. + +Одно переиспользование wall union в экспериментальном `2576d9d` дало около +10 ms и не закрыло долг. Точная доминирующая причина не доказана. Полная +Lit/DOM смена структурных шаблонов — гипотеза, а не готовое решение. + +#156 не является дублем: его regression run имел зелёный `viewToggleMs` на +другой сравнительной линии и прямо исключил #124. + +## 2. Цели + +1. Профилированием установить стоимость Flat → Iso и Iso → Flat. +2. Уложить canonical profile в неизменённый budget. +3. Сохранить Flat/Iso geometry, live layers, actions, fallback и viewport. +4. Не увеличить cache/heap и не перенести стоимость в соседнюю метрику. +5. Получить воспроизводимое exact-SHA доказательство закрытия исходного долга. + +## 3. Не входит в задачу + +- ослабление budget/noise/minimum samples; +- публичное включение изометрии; +- новая Stage 3 геометрия/материалы/камера; +- изменение Flat pixels или editors/static card; +- скрытие live layers на время toggle; +- async loading/network request; +- замена renderer architecture без измеренной причины; +- переоткрытие #89 или поглощение #156. + +## 4. Канонический benchmark + +`demo/benchmark_large_house.mjs --profile=large-house-isometric-v1` остаётся +authority. `viewToggleMs` включает полный user-observable цикл: + +1. `_setProjection('flat')`; +2. `updateComplete`; +3. `_setProjection('iso')`; +4. `updateComplete`. + +Нельзя завершить metric до painted DOM либо заменить его pure geometry timing. +Minimum — 7 samples на Linux CI с обычной environment/provenance проверкой. + +Для закрытия нужны два сравнения: + +- **debt closure:** candidate против pre-#89 SHA + `316ee76a2913066f8ccd86d27fdf7b17dabaff48`, который использовался как + историческая граница до merge #89; +- **current regression:** тот же exact candidate против актуального `main`/stable + обычным workflow, чтобы оптимизация не ухудшила v1.64 соседние paths. + +Если automation не умеет сохранить pinned comparison artifact, допускается +manual workflow input с exact SHA и ссылка на run в review. Один зелёный push +candidate-vs-parent, где product code одинаков, не закрывает #124. + +## 5. Обязательное профилирование до изменения продукта + +До выбора реализации author публикует в issue/review: + +- отдельные Flat commit и Iso commit timings; +- main-thread flame/trace либо эквивалентные измеренные call stacks; +- Lit `performUpdate`/template commit долю; +- geometry/cache hit/miss и `_isoScene()` build count; +- DOM node add/remove count по классам слоёв; +- layout/style/paint долю; +- минимум три главных cost centres с абсолютным временем. + +Профилирование выполняется на production bundle и той же fixture. Dev build, +один sample или ручное ощущение не являются доказательством. + +Diagnostic instrumentation: + +- живёт в benchmark/harness либо за test-only hook; +- не создаёт production console noise; +- не меняет timing path в финальном closure run; +- удаляется либо остаётся как дешёвый выключенный counter с documented owner. + +## 6. Допустимые направления оптимизации + +Конкретное направление выбирается только по §5. Допустимы: + +- стабилизация shared DOM subtree и переключение минимального structural layer; +- reuse уже вычисленной scene/union/index geometry; +- разделение structural и live templates, чтобы HA layers не пересоздавались; +- memoization чистых template inputs с bounded identity; +- уменьшение синхронного layout/measurement после projection commit; +- использование части `2576d9d`, если trace подтверждает её вклад. + +Запрещено: + +- держать два полноценных интерактивных дерева, если это дублирует tab order, + actions, ids или нарушает heap budget; +- CSS screenshot/bitmap вместо живого плана; +- пропуск `updateComplete`/paint в benchmark; +- debounce, который делает UI быстрым только до позднего тяжёлого commit; +- global cache без лимита; +- UA-specific fast path. + +## 7. Render и DOM invariants + +- Labs off/expired: iso DOM, geometry work, storage/network path отсутствуют; +- Flat остаётся reference и не получает Stage 2 DOM/classes/filters; +- editors и `houseplan-space-card` всегда Flat; +- Iso использует одну canonical scene/projection snapshot; +- room fills/hover, Glow/spill, sun, decor/backdrop, vacuum, devices, labels и + opening live panels сохраняются; +- DOM/tab order и actions совпадают по semantic targets; +- no duplicate ids, focus targets или pointer consumers; +- toggle сохраняет logical center и zoom по #89; +- failure latch и explicit retry остаются работоспособными. + +Если shared subtree остаётся смонтированным между projections, его inertness, +ARIA и pointer policy должны быть доказаны, а не основаны только на opacity. + +## 8. Cache и lifecycle + +- `_isoGeometryCache` cap остаётся 8; +- `cacheGrowth.isoGeometry` после profile — 0; +- repeated Flat ↔ Iso не добавляет новые entries при том же fingerprint; +- HA-only state update не rebuild-ит structural geometry; +- theme/locale/live state не меняют geometry key; +- config geometry change инвалидирует правильную scene; +- Labs expiry/removal немедленно делает effective Flat и не удерживает active + iso DOM вне bounded cache; +- warm remount не усыновляет viewport другой projection. + +## 9. Соседние performance budgets + +Оптимизация не считается успешной, если `viewToggleMs` зелёный, но ухудшены: + +- model ready / first stable render; +- space switch; +- HA state update; +- pan/zoom и resize preview; +- switch cycle; +- long-task count/max/total; +- heap growth; +- Flat `large-house-v1`. + +Все лимиты читаются из текущих versioned budget files; hardcoded threshold в +production/test не добавляется. + +## 10. Touch, accessibility и actions + +- projection toggle остаётся hidden Labs control; +- touch/kiosk gestures имеют прежний safety contract; +- активный dialog/tooltip/focus не дублируется между trees; +- tap/long press на room/device/opening вызывает тот же outcome в Flat/Iso; +- locks/security и service target не меняются; +- keyboard tab order не получает скрытых duplicate nodes; +- reduced motion не относится к мгновенному projection toggle и не меняет + benchmark semantics. + +## 11. Acceptance criteria + +1. Profiling evidence на production fixture публикует измеренную root cause. +2. Pinned pre-#89 exact comparison зелёный по неизменённому `viewToggleMs` budget + с ≥7 samples. +3. Current-main exact comparison также зелёный. +4. Все остальные timing/long-task/heap/cache checks профиля зелёные. +5. Flat profile остаётся в budget. +6. `isoGeometry` cap/growth равны 8/0, repeated toggle — cache hit. +7. Flat/Iso golden и live/touch/action smokes зелёные без несвязанной + переакцептации. +8. Labs off не создаёт iso work/DOM. +9. HA state update не rebuild-ит geometry и не переносит cost из toggle. +10. Viewport/fallback/warm-remount contract #89/#122 не регрессирует. + +## 12. План тестирования + +### Unit/source contract + +- cache key/cap/hit/invalidation; +- effective projection/fallback; +- shared DOM ownership и отсутствие duplicate consumers; +- Labs-off source contract; +- logical center conversion unchanged. + +### Browser smoke + +- repeated Flat ↔ Iso с layer/action parity; +- room/device/opening/vacuum interactions; +- Glow/sun/room fills remain mounted and state-stable; +- theme/state update during/after toggle; +- editor round-trip always Flat; +- kiosk/touch and warm remount; +- injected geometry exception and explicit retry. + +### Golden + +- все existing Flat и Iso baselines verify; +- никакой baseline не принимается только ради performance fix; +- если measured solution намеренно меняет DOM без pixels, expected diff = 0. + +### Performance + +- local 1-sample diagnostic допустим только для итерации; +- closure evidence — два Linux exact-SHA runs по §4, ≥7 samples; +- artifacts содержат raw samples, summaries, provenance и cache counters. + +## 13. План реализации + +1. Добавить/выполнить profiling instrumentation и опубликовать вывод. +2. Выбрать минимальный measured optimization. +3. Зафиксировать structural/live DOM и cache invariants тестами. +4. Реализовать без visual/action diff. +5. Прогнать typecheck, unit и build в цикле. +6. Перед beta прогнать targeted smoke/golden и оба performance comparison. + +## 14. Документация и release-артефакты + +- changelog не нужен при чистой performance-починке скрытой функции + (`User-Visible: no`); +- `docs/ISOMETRIC.md` обновляется measured architecture/root cause; +- ADR меняется, только если renderer ownership действительно изменён; +- `docs/TESTING.md`/performance README фиксируют pinned closure evidence; +- user guide и i18n не меняются; +- performance artifacts и review links обязательны; +- golden обновлять запрещено без отдельного visual rationale. + +## 15. Риски и откат + +| Риск | Мера | +| --- | --- | +| Cost просто отложен после metric | painted completion + trace | +| Два дерева дублируют actions | DOM/ARIA/pointer contract smoke | +| Cache растёт | cap/growth exact assertions | +| Flat подорожал | отдельный flat profile | +| Решение основано на гипотезе | profiling до product diff | + +Откат возвращает прежний render path. Cache/storage schema не меняются. Если +оптимизация ломает Flat/actions, Labs kill switch немедленно даёт effective Flat, +но это mitigation, а не критерий принятия красного кода. + +## 16. Принятые технические предположения + +- историческая debt-граница — exact pre-#89 SHA `316ee76…`; +- benchmark продолжает измерять полный Flat → Iso → Flat/Iso user cycle; +- measured solution может не использовать `2576d9d`; +- public activation изометрии остаётся отдельной будущей задачей. diff --git a/docs/specs/README.md b/docs/specs/README.md index 14e8aaaa..7d5f0578 100644 --- a/docs/specs/README.md +++ b/docs/specs/README.md @@ -84,6 +84,7 @@ GitHub Issues и GitHub Projects (v2) остаются единственным | [#107](https://github.com/Matysh/houseplan-card/issues/107) Переключение виртуального источника света «Всегда» | [107-virtual-light-toggle.md](107-virtual-light-toggle.md) | | [#122](https://github.com/Matysh/houseplan-card/issues/122) Изометрический режим Stage 2: скрытый режим и визуальная полировка | [122-isometric-stage2.md](122-isometric-stage2.md) | | [#123](https://github.com/Matysh/houseplan-card/issues/123) Split из вершины не меняет наружную геометрию стен | [123-corner-split-wall.md](123-corner-split-wall.md) | +| [#124](https://github.com/Matysh/houseplan-card/issues/124) Performance переключения Flat/Iso | [124-isometric-view-toggle-performance.md](124-isometric-view-toggle-performance.md) | | [#137](https://github.com/Matysh/houseplan-card/issues/137) Узлы и линии привязки в редакторе Плана | [137-plan-snap-overlay.md](137-plan-snap-overlay.md) | | [#141](https://github.com/Matysh/houseplan-card/issues/141) Бесшовные стыки перегородок и открытых контуров | [141-wall-junctions.md](141-wall-junctions.md) |