| 1 | <!-- English translation of adr/0049-skia-surface-rollout-over-avalonia-host.md. Canonical Russian: ../../adr/0049-skia-surface-rollout-over-avalonia-host.md --> |
| 2 | |
| 3 | # ADR 0049: Phased rollout of Skia-surfaces with Avalonia-host (CIDE-wide) |
| 4 | |
| 5 | **Status:** Accepted (partially: chat surface, SkiaKit; other surfaces - by waves) |
| 6 | **Date:** 2026-04-15 |
| 7 | |
| 8 | ## Related ADRs |
| 9 | |
| 10 | | ADR | Role | |
| 11 | |-----|------| |
| 12 | | [0036](0036-cds-channel-compositor-surface-pipeline.md) | channel -> CDS -> composer -> surface | |
| 13 | | [0044](0044-avalonia-host-skia-agent-chat-surface.md) | Skia hypothesis in chat | |
| 14 | | [0047](0047-cockpit-instrument-descriptor-and-slot-composition.md) | instrument/slot | |
| 15 | | [0046](0046-presentation-layout-authority-and-cockpit-invariants.md) | P/F/M invariants | |
| 16 | | [0017](0017-multi-window-workspace-and-agent-surfaces.md) | topology of windows and surfaces | |
| 17 | | [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md) | strict PFD surface | |
| 18 | |
| 19 | ## Summary |
| 20 | |
| 21 | - Phased **rollout of Skia surfaces** with Avalonia as the host (dual-path, waves). |
| 22 | - Not big-bang: migration by instruments/zones with preservation of fallback to Avalonia controls. |
| 23 | - Link with pipeline Intent→Declutter→Layout→Render ([0055](0055-skia-instrument-composition-pipeline.md)). |
| 24 | |
| 25 | --- |
| 26 | ## Context |
| 27 | |
| 28 | An experiment with Skia in the chat confirmed the practical usefulness of custom rendering for dense agent-first surfaces. The next step is not a point spike, but controlled migration across CIDE zones without losing the stability of the host layer. |
| 29 | |
| 30 | The risk of “rewriting the entire UI in Skia” is that different responsibilities will fall into one basket: |
| 31 | |
| 32 | - host/runtime windows (focus, input routing, dialogs, life cycle); |
| 33 | - cabin semantics (CDS, policy, intent, slot composition); |
| 34 | - drawing of individual surfaces. |
| 35 | |
| 36 | This ADR captures the rollout strategy: expand Skia where it really simplifies the surface layer, and not blur the boundaries already established by previous ADRs. |
| 37 | |
| 38 | ## Solution |
| 39 | |
| 40 | <a id="adr0049-p1"></a> |
| 41 | |
| 42 | 1. **The layer model remains unchanged:** |
| 43 | - **Avalonia** = host/fuselage (windows, input, focus, lifecycle, system controls); |
| 44 | - **CDS/composer** = source of semantics “what and where to show”; |
| 45 | - **Skia** = implementation of surface rendering where it is beneficial. |
| 46 | |
| 47 | <a id="adr0049-p2"></a> |
| 48 | |
| 49 | 2. **Rollout is done in waves (strangler), not big bang:** |
| 50 | - Wave 1: indicator/dense read-mostly surfaces (status bars, cockpit cards, overlays); |
| 51 | - Wave 2: MFD pages with custom geometry; |
| 52 | - Wave 3: strictly marked PFD surfaces (in conjunction with [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md)). |
| 53 | |
| 54 | <a id="adr0049-p3"></a> |
| 55 | |
| 56 | 3. **For each zone a dual-path is required:** feature flag + fallback to the current Avalonia view until stability is confirmed. Removal of fallback is allowed only after stabilization and measurements. |
| 57 | |
| 58 | <a id="adr0049-p4"></a> |
| 59 | |
| 60 | 4. **Migration unit = surface-host contract, not “control”:** Each migration relies on an explicit DTO/frame from the CDS/composer (for example slot/instrument descriptors), and not on direct dependencies on an arbitrary control tree. |
| 61 | |
| 62 | <a id="adr0049-p5"></a> |
| 63 | |
| 64 | 5. **Areas that are not migrated to baseline:** |
| 65 | - host editor and system dialogs; |
| 66 | - global window chrome; |
| 67 | - UX, where standard Avalonia controls are already sufficient and do not create bottlenecks. |
| 68 | |
| 69 | <a id="adr0049-p6"></a> |
| 70 | |
| 71 | 6. **Done criteria for each migration:** |
| 72 | - visual and behavioral parity with the current surface; |
| 73 | - no regressions in input/focus/navigation; |
| 74 | - measurable gain (readability/density/supportability, if necessary - perf); |
| 75 | - preservation of CDS/presentation invariants. |
| 76 | |
| 77 | <a id="adr0049-p7"></a> |
| 78 | |
| 79 | 7. **A separate Roslyn-guardrail is being introduced for Skia-surface (working ID: CASCOPE004):** |
| 80 | - goal: to prevent host/runtime logic from being dragged into the surface layer; |
| 81 | - scope: files/types marked as Skia-surface (convention or attribute - by implementation); |
| 82 | - starting mode: warning/info at the rollout stage; |
| 83 | - target mode: error after stabilizing the first wave and clearing current violations; |
| 84 | - minimum starting set of checks: |
| 85 | - prohibition of direct dependencies on `MainWindowViewModel` and other "fat" VMs; |
| 86 | - prohibition of direct control of `Window`/`TopLevel`/dialog lifecycle from surface types; |
| 87 | - allowing only contract DTOs from CDS/composer and render utilities without host-side effects. |
| 88 | |
| 89 | <a id="adr0049-p8"></a> |
| 90 | 8. **Basic mount-style for production presets (`instrument_id -> slot_id`):** |
| 91 | - `heavy/workflow` tools (for example Solution Explorer, settings, heavy interactive panels) -> `mfd`; |
| 92 | - `sa` tools (increasing situational awareness: navigation, status, early signals "what's happening/what's next") -> `pfd`; |
| 93 | - `hybrid` tools -> main slot `mfd`, in `pfd` only compact `read-mostly` projection (summary/badge) is allowed, without full heavy UX; |
| 94 | - `forward` is not used by default for these tools: it is the working axis of the editor, deviations are only a separate solution. |
| 95 | |
| 96 | <a id="adr0049-p9"></a> |
| 97 | |
| 98 | 9. **The scope of CASCOPE004 is fixed as hybrid:** |
| 99 | - primary scope: by namespace/folder (for example `Cockpit/Surface/**`, `*.Skia*`) for automatic coverage of the base zone; |
| 100 | - explicit include: `SkiaSurface` attribute for types outside the primary scope; |
| 101 | - explicit escape: `SkiaSurfaceHostEscape` attribute (rare, documented case) for a controlled exception; |
| 102 | - analyzer rule: triggered when `(in primary scope OR [SkiaSurface]) AND NOT [SkiaSurfaceHostEscape]`. |
| 103 | |
| 104 | <a id="adr0049-p10"></a> |
| 105 | |
| 106 | 10. **`instrument_id -> instrument_class` is entered as incremental registry v1 (source of truth):** |
| 107 | - format: separate registry (for example `instrument-classification.toml`) in the cockpit policy layer; |
| 108 | - scope v1: mandatory entry only for new and significantly changed `instrument_id`, without an instant “total freeze” for the entire legacy; |
| 109 | - enforcement: start with warning (missing classification), then switch to stricter mode according to rollout waves; |
| 110 | - goal: a single classification (`heavy` / `sa` / `hybrid`) for mount-style and guardrail checks without spreading over verbal agreements. |
| 111 | |
| 112 | <a id="adr0049-p11"></a> |
| 113 | |
| 114 | 11. **For `slot_id=pfd`, strict marking is not overridden in this ADR:** |
| 115 | - criteria and mode `PfdStrict` are set by ADR [0037](0037-pfd-surface-invariants-and-roslyn-enforcement.md); |
| 116 | - the composition of instruments and the layout `instrument_id -> slot_id` are set by the composer layer/registry according to ADR [0047](0047-cockpit-instrument-descriptor-and-slot-composition.md); |
| 117 | - ADR 0049 does not duplicate these rules, but uses them as the regulatory basis for Skia’s rollout policy. |
| 118 | |
| 119 | <a id="adr0049-p12"></a> |
| 120 | |
| 121 | 12. **Minimum perf-gate v1 for removing fallback (without performance analyzers):** |
| 122 | - there are no functional regressions in key zone scenarios (opening, switching, entering, focus); |
| 123 | - there are no consistently reproducible visual artifacts with standard resize/theme switches; |
| 124 | - there are no consistently reproducible crashes/freezes in smoke scenarios; |
| 125 | - according to the team’s working assessment, the new surface does not feel slower than the baseline on the target hardware; |
| 126 | - the surface has passed the agreed minimum of real sessions without falling back to fallback (the threshold is set in task/iteration). |
| 127 | - performance analyzers/profiling metrics are considered deferred until the rollout stabilization stage. |
| 128 | |
| 129 | <a id="adr0049-p13"></a> |
| 130 | |
| 131 | 13. **Minimum policy `SkiaSurfaceHostEscape`:** |
| 132 | - the attribute is allowed only as a temporary exception for the unblock task, if the zone does not start without host access; |
| 133 | - each exception must contain: |
| 134 | - `reason` (in short: what exactly is blocking the clean surface path); |
| 135 | - link to task/issue/ADR note; |
| 136 | - target withdrawal period (`remove_by` - iteration or date); |
| 137 | - exceptions without these fields are considered a violation of policy and must be deleted before merge; |
| 138 | - extension of the period is allowed only by a separate review solution with an update of the `reason` and links. |
| 139 | |
| 140 | <a id="adr0049-p14"></a> |
| 141 | 14. **Design is considered as a measurable SA-hypothesis (L1/L2/L3), and not just as an engineering rollout:** |
| 142 | - each `mount_style` must have target scripts and an SA profile by level: |
| 143 | - **L1 (Perception):** what signals the operator should notice; |
| 144 | - **L2 (Comprehension):** what connections/meaning should be correctly collected; |
| 145 | - **L3 (Projection):** what immediate developments of the situation should be predicted. |
| 146 | - acceptance/removal of fallback for policy is allowed only after checking against a battery of metrics: |
| 147 | - SA metrics (objective probes, freeze/query if necessary); |
| 148 | - workload (separate from SA); |
| 149 | - performance (separate from SA/workload). |
| 150 | - single-score policy is prohibited: aggregation into one number without a breakdown by L1/L2/L3 and scenarios is considered insufficient. |
| 151 | - if a policy improves one layer locally, but degrades the global SA picture (for example, attention tunneling between P/F/M), the policy is not considered ready for production preset. |
| 152 | |
| 153 | ## Consequences |
| 154 | |
| 155 | - Skia becomes a standard tool for the CIDE surface layer, but not a replacement for the entire UI platform. |
| 156 | - Technical debt is controlled through wave rollout and dual-path. |
| 157 | - Architecture invariants (0036/0046/0047) are not blurred for the sake of migration speed. |
| 158 | - Rollout gets explicit SA-gates: design solutions are tested as hypotheses with measurable effect, and not just against engineering/visual criteria. |
| 159 | |
| 160 | ## Open questions |
| 161 | |
| 162 | - At the time of this ADR there are no open questions; further clarifications are recorded in separate ADR updates. |