13 KiB
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. Цели
- Профилированием установить стоимость Flat → Iso и Iso → Flat.
- Уложить canonical profile в неизменённый budget.
- Сохранить Flat/Iso geometry, live layers, actions, fallback и viewport.
- Не увеличить cache/heap и не перенести стоимость в соседнюю метрику.
- Получить воспроизводимое 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 цикл:
_setProjection('flat');updateComplete;_setProjection('iso');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
_isoGeometryCachecap остаётся 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
- Profiling evidence на production fixture публикует измеренную root cause.
- Pinned pre-#89 exact comparison зелёный по неизменённому
viewToggleMsbudget с ≥7 samples. - Current-main exact comparison также зелёный.
- Все остальные timing/long-task/heap/cache checks профиля зелёные.
- Flat profile остаётся в budget.
isoGeometrycap/growth равны 8/0, repeated toggle — cache hit.- Flat/Iso golden и live/touch/action smokes зелёные без несвязанной переакцептации.
- Labs off не создаёт iso work/DOM.
- HA state update не rebuild-ит geometry и не переносит cost из toggle.
- 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. План реализации
- Добавить/выполнить profiling instrumentation и опубликовать вывод.
- Выбрать минимальный measured optimization.
- Зафиксировать structural/live DOM и cache invariants тестами.
- Реализовать без visual/action diff.
- Прогнать typecheck, unit и build в цикле.
- Перед 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 изометрии остаётся отдельной будущей задачей.