Compare commits

...
Author SHA1 Message Date
Sergey Matyunin d6ec494a1a docs: specify isometric toggle performance
Issue: #124
User-Visible: no
2026-08-15 03:50:34 +03:00
2 changed files with 255 additions and 0 deletions
@@ -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 изометрии остаётся отдельной будущей задачей.
+1
View File
@@ -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) |