| 1 | # ADR 0049: Поэтапный rollout Skia-поверхностей при Avalonia-host (CIDE-wide) |
| 2 | |
| 3 | **Статус:** Accepted (частично: chat surface, SkiaKit; остальные поверхности — по волнам) |
| 4 | **Дата:** 2026-04-15 |
| 5 | |
| 6 | ## Связанные ADR |
| 7 | |
| 8 | | ADR | Роль | |
| 9 | |-----|------| |
| 10 | | [0036](0036-cds-channel-compositor-surface-pipeline.md) | канал -> CDS -> композитор -> поверхность | |
| 11 | | [0044](0044-avalonia-host-skia-agent-chat-surface.md) | гипотеза Skia в чате | |
| 12 | | [0047](0047-cockpit-instrument-descriptor-and-slot-composition.md) | instrument/slot | |
| 13 | | [0046](0046-presentation-layout-authority-and-cockpit-invariants.md) | инварианты P/F/M | |
| 14 | | [0017](0017-multi-window-workspace-and-agent-surfaces.md) | топология окон и surfaces | |
| 15 | | [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md) | строгая PFD-поверхность | |
| 16 | |
| 17 | ## Резюме |
| 18 | |
| 19 | - Поэтапный **rollout Skia-поверхностей** при Avalonia как хосте (dual-path, волны). |
| 20 | - Не big-bang: миграция по инструментам/зонам с сохранением fallback на Avalonia-контролы. |
| 21 | - Связка с pipeline Intent→Declutter→Layout→Render ([0055](0055-skia-instrument-composition-pipeline.md)). |
| 22 | |
| 23 | --- |
| 24 | ## Контекст |
| 25 | |
| 26 | Эксперимент со Skia в чате подтвердил практическую полезность кастомной отрисовки для плотных agent-first поверхностей. Следующий шаг - не точечный спайк, а управляемая миграция по зонам CIDE без потери стабильности host-слоя. |
| 27 | |
| 28 | Риск "переписать все UI на Skia" в том, что в одну корзину попадут разные ответственности: |
| 29 | |
| 30 | - host/runtime окна (focus, input routing, диалоги, жизненный цикл); |
| 31 | - семантика кабины (CDS, policy, intent, slot composition); |
| 32 | - отрисовка отдельных поверхностей. |
| 33 | |
| 34 | Этот ADR фиксирует rollout-стратегию: расширять Skia там, где она реально упрощает surface-слой, и не размывать границы, уже закрепленные предыдущими ADR. |
| 35 | |
| 36 | ## Решение |
| 37 | |
| 38 | <a id="adr0049-p1"></a> |
| 39 | |
| 40 | 1. **Модель слоев остается неизменной:** |
| 41 | - **Avalonia** = host/fuselage (окна, ввод, фокус, lifecycle, системные контролы); |
| 42 | - **CDS/композитор** = источник семантики "что и где показывать"; |
| 43 | - **Skia** = реализация surface-отрисовки там, где это выгодно. |
| 44 | |
| 45 | <a id="adr0049-p2"></a> |
| 46 | |
| 47 | 2. **Rollout делается волнами (strangler), не big bang:** |
| 48 | - Wave 1: индикаторные/плотные read-mostly поверхности (status bars, cockpit cards, overlays); |
| 49 | - Wave 2: MFD-страницы с кастомной геометрией; |
| 50 | - Wave 3: строго маркированные PFD-поверхности (в связке с [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md)). |
| 51 | |
| 52 | <a id="adr0049-p3"></a> |
| 53 | |
| 54 | 3. **Для каждой зоны обязателен dual-path:** feature flag + fallback на текущий Avalonia view до подтвержденной стабильности. Удаление fallback разрешено только после стабилизации и измерений. |
| 55 | |
| 56 | <a id="adr0049-p4"></a> |
| 57 | |
| 58 | 4. **Migration unit = surface-host contract, не "контрол":** каждая миграция опирается на явный DTO/кадр из CDS/композитора (например slot/instrument descriptors), а не на прямые зависимости от произвольного дерева контролов. |
| 59 | |
| 60 | <a id="adr0049-p5"></a> |
| 61 | |
| 62 | 5. **Области, которые не мигрируются в baseline:** |
| 63 | - редактор-хост и системные диалоги; |
| 64 | - глобальный window chrome; |
| 65 | - UX, где стандартные Avalonia-контролы уже достаточны и не создают узких мест. |
| 66 | |
| 67 | <a id="adr0049-p6"></a> |
| 68 | |
| 69 | 6. **Критерии "готово" для каждой миграции:** |
| 70 | - визуальный и поведенческий паритет с текущей поверхностью; |
| 71 | - отсутствие регрессий по input/focus/navigation; |
| 72 | - измеряемый выигрыш (читабельность/плотность/поддерживаемость, при необходимости - perf); |
| 73 | - сохранение инвариантов CDS/presentation. |
| 74 | |
| 75 | <a id="adr0049-p7"></a> |
| 76 | |
| 77 | 7. **Вводится отдельный Roslyn-guardrail для Skia-surface (рабочий ID: CASCOPE004):** |
| 78 | - цель: не допускать протаскивание host/runtime-логики в surface-слой; |
| 79 | - область действия: файлы/типы, помеченные как Skia-surface (конвенция или атрибут - по реализации); |
| 80 | - стартовый режим: warning/info на этапе rollout; |
| 81 | - целевой режим: error после стабилизации первой волны и чистки текущих нарушений; |
| 82 | - минимальный стартовый набор проверок: |
| 83 | - запрет прямых зависимостей на `MainWindowViewModel` и другие "толстые" VM; |
| 84 | - запрет прямого управления `Window`/`TopLevel`/диалоговым lifecycle из surface-типов; |
| 85 | - разрешение только контрактных DTO из CDS/композитора и рендер-утилит без host-side эффектов. |
| 86 | |
| 87 | <a id="adr0049-p8"></a> |
| 88 | |
| 89 | 8. **Базовая mount-style для production-пресетов (`instrument_id -> slot_id`):** |
| 90 | - `heavy/workflow` инструменты (например Solution Explorer, настройки, тяжёлые интерактивные панели) -> `mfd`; |
| 91 | - `sa` инструменты (повышение situational awareness: навигация, статус, ранние сигналы "что происходит/что дальше") -> `pfd`; |
| 92 | - `hybrid` инструменты -> основной слот `mfd`, в `pfd` допускается только компактная `read-mostly` проекция (summary/badge), без полного heavy UX; |
| 93 | - `forward` не используется по умолчанию для этих инструментов: это рабочая ось редактора, отклонения - только отдельным решением. |
| 94 | |
| 95 | <a id="adr0049-p9"></a> |
| 96 | |
| 97 | 9. **Область действия CASCOPE004 фиксируется как гибридная:** |
| 98 | - primary scope: по namespace/папке (например `Cockpit/Surface/**`, `*.Skia*`) для автоматического покрытия базовой зоны; |
| 99 | - explicit include: атрибут `SkiaSurface` для типов вне primary scope; |
| 100 | - explicit escape: атрибут `SkiaSurfaceHostEscape` (редкий, документированный случай) для контролируемого исключения; |
| 101 | - правило анализатора: срабатывает при `(in primary scope OR [SkiaSurface]) AND NOT [SkiaSurfaceHostEscape]`. |
| 102 | |
| 103 | <a id="adr0049-p10"></a> |
| 104 | |
| 105 | 10. **`instrument_id -> instrument_class` вводится как incremental registry v1 (source of truth):** |
| 106 | - формат: отдельный реестр (например `instrument-classification.toml`) в слое cockpit policy; |
| 107 | - scope v1: обязательная запись только для новых и существенно изменяемых `instrument_id`, без мгновенного "тотального freeze" для всего наследия; |
| 108 | - enforcement: старт с warning (missing classification), затем переход к stricter-режиму по волнам rollout; |
| 109 | - цель: единая классификация (`heavy` / `sa` / `hybrid`) для mount-style и guardrail-проверок без расползания по устным договоренностям. |
| 110 | |
| 111 | <a id="adr0049-p11"></a> |
| 112 | |
| 113 | 11. **Для `slot_id=pfd` строгая маркировка не переопределяется в этом ADR:** |
| 114 | - критерии и режим `PfdStrict` задаются ADR [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md); |
| 115 | - состав инструментов и раскладка `instrument_id -> slot_id` задаются слоем композитора/реестром по ADR [0047](0047-cockpit-instrument-descriptor-and-slot-composition.md); |
| 116 | - ADR 0049 не дублирует эти правила, а использует их как нормативную основу rollout-политики Skia. |
| 117 | |
| 118 | <a id="adr0049-p12"></a> |
| 119 | |
| 120 | 12. **Минимальный perf-gate v1 для снятия fallback (без performance analyzers):** |
| 121 | - отсутствуют функциональные регрессии в ключевых сценариях зоны (открытие, переключение, ввод, фокус); |
| 122 | - отсутствуют устойчиво воспроизводимые визуальные артефакты при типовых resize/theme switch; |
| 123 | - отсутствуют устойчиво воспроизводимые крэши/зависания в smoke-сценариях; |
| 124 | - по рабочей оценке команды новая поверхность не ощущается медленнее baseline на целевом железе; |
| 125 | - поверхность прошла agreed minimum реальных сессий без отката на fallback (порог задаётся в task/итерации). |
| 126 | - performance analyzers/метрики профилирования считаются deferred до этапа стабилизации rollout. |
| 127 | |
| 128 | <a id="adr0049-p13"></a> |
| 129 | |
| 130 | 13. **Минимальная политика `SkiaSurfaceHostEscape`:** |
| 131 | - атрибут разрешён только как временное исключение для unblock задачи, если без host-доступа зона не запускается; |
| 132 | - каждое исключение обязано содержать: |
| 133 | - `reason` (кратко: что именно блокирует чистый surface-путь); |
| 134 | - ссылку на task/issue/ADR-заметку; |
| 135 | - целевой срок снятия (`remove_by` - итерация или дата); |
| 136 | - исключения без этих полей считаются нарушением политики и подлежат удалению до merge; |
| 137 | - продление срока допускается только отдельным review-решением с обновлением `reason` и ссылки. |
| 138 | |
| 139 | <a id="adr0049-p14"></a> |
| 140 | |
| 141 | 14. **Дизайн рассматривается как измеряемая SA-гипотеза (L1/L2/L3), а не только как инженерный rollout:** |
| 142 | - каждая `mount_style` обязана иметь целевые сценарии и SA-профиль по уровням: |
| 143 | - **L1 (Perception):** какие сигналы оператор должен заметить; |
| 144 | - **L2 (Comprehension):** какие связи/смысл должен корректно собрать; |
| 145 | - **L3 (Projection):** какие ближайшие развития ситуации должен предсказать. |
| 146 | - принятие/снятие fallback для policy разрешено только после проверки по батарее метрик: |
| 147 | - SA-метрики (объективные probes, при необходимости freeze/query); |
| 148 | - workload (отдельно от SA); |
| 149 | - performance (отдельно от SA/workload). |
| 150 | - single-score политика запрещена: агрегирование в одно число без разреза по L1/L2/L3 и сценариям считается недостаточным. |
| 151 | - если policy улучшает локально один слой, но деградирует глобальную SA-картину (например туннелирование внимания между P/F/M), policy не считается готовой к production preset. |
| 152 | |
| 153 | ## Последствия |
| 154 | |
| 155 | - Skia становится штатным инструментом surface-слоя CIDE, но не заменой всей UI-платформы. |
| 156 | - Технический долг контролируется через волновой rollout и dual-path. |
| 157 | - Инварианты архитектуры (0036/0046/0047) не размываются ради скорости миграции. |
| 158 | - Rollout получает явные SA-gates: дизайн-решения проверяются как гипотезы с измеримым эффектом, а не только по инженерным/визуальным критериям. |
| 159 | |
| 160 | ## Открытые вопросы |
| 161 | |
| 162 | - На момент этого ADR открытых вопросов нет; дальнейшие уточнения фиксируются отдельными ADR-обновлениями. |
| 163 | |
| 164 | |
| 165 | |