ADR 0049: Поэтапный rollout Skia-поверхностей при Avalonia-host (CIDE-wide)¶
Статус: Accepted (частично: chat surface, SkiaKit; остальные поверхности — по волнам)
Дата: 2026-04-15
Связанные ADR¶
| ADR | Роль |
|---|---|
| 0036 | канал -> CDS -> композитор -> поверхность |
| 0044 | гипотеза Skia в чате |
| 0047 | instrument/slot |
| 0046 | инварианты P/F/M |
| 0017 | топология окон и surfaces |
| 0037 | строгая PFD-поверхность |
Резюме¶
- Поэтапный rollout Skia-поверхностей при Avalonia как хосте (dual-path, волны).
- Не big-bang: миграция по инструментам/зонам с сохранением fallback на Avalonia-контролы.
- Связка с pipeline Intent→Declutter→Layout→Render (0055).
Контекст¶
Эксперимент со Skia в чате подтвердил практическую полезность кастомной отрисовки для плотных agent-first поверхностей. Следующий шаг - не точечный спайк, а управляемая миграция по зонам CIDE без потери стабильности host-слоя.
Риск "переписать все UI на Skia" в том, что в одну корзину попадут разные ответственности:
- host/runtime окна (focus, input routing, диалоги, жизненный цикл);
- семантика кабины (CDS, policy, intent, slot composition);
- отрисовка отдельных поверхностей.
Этот ADR фиксирует rollout-стратегию: расширять Skia там, где она реально упрощает surface-слой, и не размывать границы, уже закрепленные предыдущими ADR.
Решение¶
- Модель слоев остается неизменной:
- Avalonia = host/fuselage (окна, ввод, фокус, lifecycle, системные контролы);
- CDS/композитор = источник семантики "что и где показывать";
- Skia = реализация surface-отрисовки там, где это выгодно.
- Rollout делается волнами (strangler), не big bang:
- Wave 1: индикаторные/плотные read-mostly поверхности (status bars, cockpit cards, overlays);
- Wave 2: MFD-страницы с кастомной геометрией;
- Wave 3: строго маркированные PFD-поверхности (в связке с 0037).
- Для каждой зоны обязателен dual-path: feature flag + fallback на текущий Avalonia view до подтвержденной стабильности. Удаление fallback разрешено только после стабилизации и измерений.
- Migration unit = surface-host contract, не "контрол": каждая миграция опирается на явный DTO/кадр из CDS/композитора (например slot/instrument descriptors), а не на прямые зависимости от произвольного дерева контролов.
- Области, которые не мигрируются в baseline:
- редактор-хост и системные диалоги;
- глобальный window chrome;
- UX, где стандартные Avalonia-контролы уже достаточны и не создают узких мест.
- Критерии "готово" для каждой миграции:
- визуальный и поведенческий паритет с текущей поверхностью;
- отсутствие регрессий по input/focus/navigation;
- измеряемый выигрыш (читабельность/плотность/поддерживаемость, при необходимости - perf);
- сохранение инвариантов CDS/presentation.
- Вводится отдельный Roslyn-guardrail для Skia-surface (рабочий ID: CASCOPE004):
- цель: не допускать протаскивание host/runtime-логики в surface-слой;
- область действия: файлы/типы, помеченные как Skia-surface (конвенция или атрибут - по реализации);
- стартовый режим: warning/info на этапе rollout;
- целевой режим: error после стабилизации первой волны и чистки текущих нарушений;
- минимальный стартовый набор проверок:
- запрет прямых зависимостей на
MainWindowViewModelи другие "толстые" VM; - запрет прямого управления
Window/TopLevel/диалоговым lifecycle из surface-типов; - разрешение только контрактных DTO из CDS/композитора и рендер-утилит без host-side эффектов.
- запрет прямых зависимостей на
- Базовая mount-style для production-пресетов (
instrument_id -> slot_id): heavy/workflowинструменты (например Solution Explorer, настройки, тяжёлые интерактивные панели) ->mfd;saинструменты (повышение situational awareness: навигация, статус, ранние сигналы "что происходит/что дальше") ->pfd;hybridинструменты -> основной слотmfd, вpfdдопускается только компактнаяread-mostlyпроекция (summary/badge), без полного heavy UX;forwardне используется по умолчанию для этих инструментов: это рабочая ось редактора, отклонения - только отдельным решением.
- Область действия CASCOPE004 фиксируется как гибридная:
- primary scope: по namespace/папке (например
Cockpit/Surface/**,*.Skia*) для автоматического покрытия базовой зоны; - explicit include: атрибут
SkiaSurfaceдля типов вне primary scope; - explicit escape: атрибут
SkiaSurfaceHostEscape(редкий, документированный случай) для контролируемого исключения; - правило анализатора: срабатывает при
(in primary scope OR [SkiaSurface]) AND NOT [SkiaSurfaceHostEscape].
instrument_id -> instrument_classвводится как incremental registry v1 (source of truth):- формат: отдельный реестр (например
instrument-classification.toml) в слое cockpit policy; - scope v1: обязательная запись только для новых и существенно изменяемых
instrument_id, без мгновенного "тотального freeze" для всего наследия; - enforcement: старт с warning (missing classification), затем переход к stricter-режиму по волнам rollout;
- цель: единая классификация (
heavy/sa/hybrid) для mount-style и guardrail-проверок без расползания по устным договоренностям.
- Для
slot_id=pfdстрогая маркировка не переопределяется в этом ADR: - критерии и режим
PfdStrictзадаются ADR 0037; - состав инструментов и раскладка
instrument_id -> slot_idзадаются слоем композитора/реестром по ADR 0047; - ADR 0049 не дублирует эти правила, а использует их как нормативную основу rollout-политики Skia.
- Минимальный perf-gate v1 для снятия fallback (без performance analyzers):
- отсутствуют функциональные регрессии в ключевых сценариях зоны (открытие, переключение, ввод, фокус);
- отсутствуют устойчиво воспроизводимые визуальные артефакты при типовых resize/theme switch;
- отсутствуют устойчиво воспроизводимые крэши/зависания в smoke-сценариях;
- по рабочей оценке команды новая поверхность не ощущается медленнее baseline на целевом железе;
- поверхность прошла agreed minimum реальных сессий без отката на fallback (порог задаётся в task/итерации).
- performance analyzers/метрики профилирования считаются deferred до этапа стабилизации rollout.
- Минимальная политика
SkiaSurfaceHostEscape: - атрибут разрешён только как временное исключение для unblock задачи, если без host-доступа зона не запускается;
- каждое исключение обязано содержать:
reason(кратко: что именно блокирует чистый surface-путь);- ссылку на task/issue/ADR-заметку;
- целевой срок снятия (
remove_by- итерация или дата);
- исключения без этих полей считаются нарушением политики и подлежат удалению до merge;
- продление срока допускается только отдельным review-решением с обновлением
reasonи ссылки.
- Дизайн рассматривается как измеряемая SA-гипотеза (L1/L2/L3), а не только как инженерный rollout:
- каждая
mount_styleобязана иметь целевые сценарии и SA-профиль по уровням:- L1 (Perception): какие сигналы оператор должен заметить;
- L2 (Comprehension): какие связи/смысл должен корректно собрать;
- L3 (Projection): какие ближайшие развития ситуации должен предсказать.
- принятие/снятие fallback для policy разрешено только после проверки по батарее метрик:
- SA-метрики (объективные probes, при необходимости freeze/query);
- workload (отдельно от SA);
- performance (отдельно от SA/workload).
- single-score политика запрещена: агрегирование в одно число без разреза по L1/L2/L3 и сценариям считается недостаточным.
- если policy улучшает локально один слой, но деградирует глобальную SA-картину (например туннелирование внимания между P/F/M), policy не считается готовой к production preset.
Последствия¶
- Skia становится штатным инструментом surface-слоя CIDE, но не заменой всей UI-платформы.
- Технический долг контролируется через волновой rollout и dual-path.
- Инварианты архитектуры (0036/0046/0047) не размываются ради скорости миграции.
- Rollout получает явные SA-gates: дизайн-решения проверяются как гипотезы с измеримым эффектом, а не только по инженерным/визуальным критериям.
Открытые вопросы¶
- На момент этого ADR открытых вопросов нет; дальнейшие уточнения фиксируются отдельными ADR-обновлениями.