Перейти к содержанию

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.

Решение

  1. Модель слоев остается неизменной:
  2. Avalonia = host/fuselage (окна, ввод, фокус, lifecycle, системные контролы);
  3. CDS/композитор = источник семантики "что и где показывать";
  4. Skia = реализация surface-отрисовки там, где это выгодно.

  1. Rollout делается волнами (strangler), не big bang:
  2. Wave 1: индикаторные/плотные read-mostly поверхности (status bars, cockpit cards, overlays);
  3. Wave 2: MFD-страницы с кастомной геометрией;
  4. Wave 3: строго маркированные PFD-поверхности (в связке с 0037).

  1. Для каждой зоны обязателен dual-path: feature flag + fallback на текущий Avalonia view до подтвержденной стабильности. Удаление fallback разрешено только после стабилизации и измерений.

  1. Migration unit = surface-host contract, не "контрол": каждая миграция опирается на явный DTO/кадр из CDS/композитора (например slot/instrument descriptors), а не на прямые зависимости от произвольного дерева контролов.

  1. Области, которые не мигрируются в baseline:
  2. редактор-хост и системные диалоги;
  3. глобальный window chrome;
  4. UX, где стандартные Avalonia-контролы уже достаточны и не создают узких мест.

  1. Критерии "готово" для каждой миграции:
  2. визуальный и поведенческий паритет с текущей поверхностью;
  3. отсутствие регрессий по input/focus/navigation;
  4. измеряемый выигрыш (читабельность/плотность/поддерживаемость, при необходимости - perf);
  5. сохранение инвариантов CDS/presentation.

  1. Вводится отдельный Roslyn-guardrail для Skia-surface (рабочий ID: CASCOPE004):
  2. цель: не допускать протаскивание host/runtime-логики в surface-слой;
  3. область действия: файлы/типы, помеченные как Skia-surface (конвенция или атрибут - по реализации);
  4. стартовый режим: warning/info на этапе rollout;
  5. целевой режим: error после стабилизации первой волны и чистки текущих нарушений;
  6. минимальный стартовый набор проверок:
    • запрет прямых зависимостей на MainWindowViewModel и другие "толстые" VM;
    • запрет прямого управления Window/TopLevel/диалоговым lifecycle из surface-типов;
    • разрешение только контрактных DTO из CDS/композитора и рендер-утилит без host-side эффектов.

  1. Базовая mount-style для production-пресетов (instrument_id -> slot_id):
  2. heavy/workflow инструменты (например Solution Explorer, настройки, тяжёлые интерактивные панели) -> mfd;
  3. sa инструменты (повышение situational awareness: навигация, статус, ранние сигналы "что происходит/что дальше") -> pfd;
  4. hybrid инструменты -> основной слот mfd, в pfd допускается только компактная read-mostly проекция (summary/badge), без полного heavy UX;
  5. forward не используется по умолчанию для этих инструментов: это рабочая ось редактора, отклонения - только отдельным решением.

  1. Область действия CASCOPE004 фиксируется как гибридная:
  2. primary scope: по namespace/папке (например Cockpit/Surface/**, *.Skia*) для автоматического покрытия базовой зоны;
  3. explicit include: атрибут SkiaSurface для типов вне primary scope;
  4. explicit escape: атрибут SkiaSurfaceHostEscape (редкий, документированный случай) для контролируемого исключения;
  5. правило анализатора: срабатывает при (in primary scope OR [SkiaSurface]) AND NOT [SkiaSurfaceHostEscape].

  1. instrument_id -> instrument_class вводится как incremental registry v1 (source of truth):
  2. формат: отдельный реестр (например instrument-classification.toml) в слое cockpit policy;
  3. scope v1: обязательная запись только для новых и существенно изменяемых instrument_id, без мгновенного "тотального freeze" для всего наследия;
  4. enforcement: старт с warning (missing classification), затем переход к stricter-режиму по волнам rollout;
  5. цель: единая классификация (heavy / sa / hybrid) для mount-style и guardrail-проверок без расползания по устным договоренностям.

  1. Для slot_id=pfd строгая маркировка не переопределяется в этом ADR:
  2. критерии и режим PfdStrict задаются ADR 0037;
  3. состав инструментов и раскладка instrument_id -> slot_id задаются слоем композитора/реестром по ADR 0047;
  4. ADR 0049 не дублирует эти правила, а использует их как нормативную основу rollout-политики Skia.

  1. Минимальный perf-gate v1 для снятия fallback (без performance analyzers):
  2. отсутствуют функциональные регрессии в ключевых сценариях зоны (открытие, переключение, ввод, фокус);
  3. отсутствуют устойчиво воспроизводимые визуальные артефакты при типовых resize/theme switch;
  4. отсутствуют устойчиво воспроизводимые крэши/зависания в smoke-сценариях;
  5. по рабочей оценке команды новая поверхность не ощущается медленнее baseline на целевом железе;
  6. поверхность прошла agreed minimum реальных сессий без отката на fallback (порог задаётся в task/итерации).
  7. performance analyzers/метрики профилирования считаются deferred до этапа стабилизации rollout.

  1. Минимальная политика SkiaSurfaceHostEscape:
  2. атрибут разрешён только как временное исключение для unblock задачи, если без host-доступа зона не запускается;
  3. каждое исключение обязано содержать:
    • reason (кратко: что именно блокирует чистый surface-путь);
    • ссылку на task/issue/ADR-заметку;
    • целевой срок снятия (remove_by - итерация или дата);
  4. исключения без этих полей считаются нарушением политики и подлежат удалению до merge;
  5. продление срока допускается только отдельным review-решением с обновлением reason и ссылки.

  1. Дизайн рассматривается как измеряемая SA-гипотеза (L1/L2/L3), а не только как инженерный rollout:
  2. каждая mount_style обязана иметь целевые сценарии и SA-профиль по уровням:
    • L1 (Perception): какие сигналы оператор должен заметить;
    • L2 (Comprehension): какие связи/смысл должен корректно собрать;
    • L3 (Projection): какие ближайшие развития ситуации должен предсказать.
  3. принятие/снятие fallback для policy разрешено только после проверки по батарее метрик:
    • SA-метрики (объективные probes, при необходимости freeze/query);
    • workload (отдельно от SA);
    • performance (отдельно от SA/workload).
  4. single-score политика запрещена: агрегирование в одно число без разреза по L1/L2/L3 и сценариям считается недостаточным.
  5. если policy улучшает локально один слой, но деградирует глобальную SA-картину (например туннелирование внимания между P/F/M), policy не считается готовой к production preset.

Последствия

  • Skia становится штатным инструментом surface-слоя CIDE, но не заменой всей UI-платформы.
  • Технический долг контролируется через волновой rollout и dual-path.
  • Инварианты архитектуры (0036/0046/0047) не размываются ради скорости миграции.
  • Rollout получает явные SA-gates: дизайн-решения проверяются как гипотезы с измеримым эффектом, а не только по инженерным/визуальным критериям.

Открытые вопросы

  • На момент этого ADR открытых вопросов нет; дальнейшие уточнения фиксируются отдельными ADR-обновлениями.