Forge
markdowndeeb25a2
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
401. **Модель слоев остается неизменной:**
41 - **Avalonia** = host/fuselage (окна, ввод, фокус, lifecycle, системные контролы);
42 - **CDS/композитор** = источник семантики "что и где показывать";
43 - **Skia** = реализация surface-отрисовки там, где это выгодно.
44
45<a id="adr0049-p2"></a>
46
472. **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
543. **Для каждой зоны обязателен dual-path:** feature flag + fallback на текущий Avalonia view до подтвержденной стабильности. Удаление fallback разрешено только после стабилизации и измерений.
55
56<a id="adr0049-p4"></a>
57
584. **Migration unit = surface-host contract, не "контрол":** каждая миграция опирается на явный DTO/кадр из CDS/композитора (например slot/instrument descriptors), а не на прямые зависимости от произвольного дерева контролов.
59
60<a id="adr0049-p5"></a>
61
625. **Области, которые не мигрируются в baseline:**
63 - редактор-хост и системные диалоги;
64 - глобальный window chrome;
65 - UX, где стандартные Avalonia-контролы уже достаточны и не создают узких мест.
66
67<a id="adr0049-p6"></a>
68
696. **Критерии "готово" для каждой миграции:**
70 - визуальный и поведенческий паритет с текущей поверхностью;
71 - отсутствие регрессий по input/focus/navigation;
72 - измеряемый выигрыш (читабельность/плотность/поддерживаемость, при необходимости - perf);
73 - сохранение инвариантов CDS/presentation.
74
75<a id="adr0049-p7"></a>
76
777. **Вводится отдельный 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
898. **Базовая 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
979. **Область действия 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
10510. **`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
11311. **Для `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
12012. **Минимальный 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
13013. **Минимальная политика `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
14114. **Дизайн рассматривается как измеряемая 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
View only · write via MCP/CIDE