| 1 | # ADR 0024: SDK для CascadeIDE — стабильные контракты для внутреннего расширения и будущих плагинов |
| 2 | |
| 3 | **Статус:** Proposed |
| 4 | **Дата:** 2026-04-08 |
| 5 | |
| 6 | ## Связанные ADR |
| 7 | |
| 8 | | ADR | Роль | |
| 9 | |-----|------| |
| 10 | | [0005](0005-defer-dynamic-plugins-mef.md) | плагины deferred | |
| 11 | | [0006](0006-presentation-layers-and-feature-slices.md) | границы слоёв/срезов | |
| 12 | | [0008](0008-mcp-contracts-and-testable-infrastructure.md) | контракты и тестируемая инфраструктура | |
| 13 | | [0013](0013-command-surface-and-discoverability.md) | поверхность команд | |
| 14 | | [0019](0019-shared-git-core-ide-and-git-mcp.md) | общий core как прецедент | |
| 15 | | [0023](0023-markdown-diagrams-language-tooling.md) | внешние инструменты/LSP как контракты | |
| 16 | | [0026](0026-markdown-preview-surfaces-and-placement.md) | геометрия превью Markdown в TOML | |
| 17 | | [0025](0025-sdk-attention-zones-and-capabilities.md) | зоны внимания PFD/MFD/… в SDK и capabilities | |
| 18 | |
| 19 | ## Резюме |
| 20 | |
| 21 | - **CascadeIDE SDK** — стабильные контракты для внутреннего расширения и будущих плагинов. |
| 22 | - Версионирование публичных API; граница с «всё internal». |
| 23 | - Связь с зонами внимания — [0025](0025-sdk-attention-zones-and-capabilities.md). |
| 24 | |
| 25 | --- |
| 26 | ## Контекст |
| 27 | |
| 28 | CascadeIDE активно развивается “изнутри”: добавляются новые панели, инструменты, каналы диагностики, режимы UI и интеграции с внешними процессами (LSP/DAP/MCP). При этом: |
| 29 | |
| 30 | - Удобство разработки упирается в **ментальную модель**: где границы, какие зависимости допустимы, что считается “ядром”, что — “фича”. |
| 31 | - `MainWindowViewModel` и UI-shell неизбежно становятся местом, куда “просится всё”, если нет явных контрактов. |
| 32 | - Плагины (динамическая загрузка DLL/MEF) пока **отложены** ([0005](0005-defer-dynamic-plugins-mef.md)), но когда мы к ним вернёмся, нужен будет “куда вставлять”, иначе plugin-host появится раньше, чем стабилизируется форма слотов/контрактов. |
| 33 | |
| 34 | Нужен “SDK” — но в первую очередь **для нас же**, чтобы: |
| 35 | |
| 36 | - уменьшить связность между фичами, |
| 37 | - обеспечить расширяемость без хаоса, |
| 38 | - подготовить базу под будущие плагины без преждевременного plugin-host. |
| 39 | |
| 40 | --- |
| 41 | |
| 42 | ## Решение |
| 43 | |
| 44 | Принять подход: **SDK = стабильные контракты расширения IDE**, а не обязательная система плагинов сегодня. |
| 45 | |
| 46 | SDK в v1 включает: |
| 47 | |
| 48 | 1. **Явные публичные интерфейсы/контракты** между shell и фичами (и между фичами), оформленные как отдельный слой (папка/проект), с минимальными зависимостями. |
| 49 | - Формат поставки: **отдельный проект** `CascadeIDE.Contracts` (или близкое имя), а не папка внутри `cascade-ide`. |
| 50 | |
| 51 | 2. **Capability-модель** (объявление возможностей), чтобы фича могла “подключаться” к shell и друг к другу без прямых ссылок на конкретные реализации. |
| 52 | |
| 53 | 3. **Capability registry — hybrid (следует из текущей логики IDE):** |
| 54 | - **Code-first регистрация** capabilities фичами при старте (без MEF/рефлексии “на вырост”). |
| 55 | - **Data overlay для презентации**: UI-режимы/раскладки (TOML) могут включать/выключать или менять presentation capabilities, но не подменяют их семантику. |
| 56 | - **Introspection**: shell умеет собрать “карту возможностей” для диагностики/телеметрии/агента. |
| 57 | |
| 58 | 4. **Command surface как контракт**: команды (и их discoverability) оформляются не через “познавание внутренностей” фичи, а через общий контракт регистрации/метаданных (в духе [0013](0013-command-surface-and-discoverability.md)). |
| 59 | |
| 60 | 5. **Out-of-proc как норма для интеграций** (LSP/DAP/MCP/CLI): протоколы и DTO — часть “SDK” в смысле стабильных контрактов (см. [0008](0008-mcp-contracts-and-testable-infrastructure.md)). In-proc расширения не запрещены, но должны входить в рамки контрактов. |
| 61 | |
| 62 | 6. **Маркировка контрактов `Experimental`/`Stable` — внутренняя ясность, не публичное обещание.** |
| 63 | На стадии active-dev (далеко до alpha) цель — не “совместимость навсегда”, а дисциплина границ: по умолчанию всё `Experimental`, а `Stable` появляется только там, где команда осознанно хочет опираться на контракт. Ломать `Experimental` можно свободно; ломать `Stable` — осознанно (через ADR/заметку о миграции), SemVer — когда появится продуктовая потребность. |
| 64 | |
| 65 | 7. **Отложенный plugin-host**: динамическая загрузка плагинов остаётся deferred (см. [0005](0005-defer-dynamic-plugins-mef.md)), но будущий plugin-host обязан подключаться через те же контракты SDK, что и “внутренние” фичи. |
| 66 | |
| 67 | ### Модель внимания кокпита (PFD / MFD / Forward / EICAS / HUD) и SDK |
| 68 | |
| 69 | Семантика зон описана в [0021](0021-pfd-mfd-cockpit-attention-model.md). **Явная привязка UI‑capabilities к зонам внимания на уровне `CascadeIDE.Contracts`** — отдельное решение и поэтапная реализация: [0025](0025-sdk-attention-zones-and-capabilities.md). Так [0024](0024-ide-sdk-and-stable-contracts.md) остаётся про общий SDK, а кокпитная ось не смешивается с registry/plugin‑обсуждением. |
| 70 | |
| 71 | --- |
| 72 | |
| 73 | ## Capability registry / capability-map (API и принципы) |
| 74 | |
| 75 | Цель: capability-слой должен быть достаточно строгим, чтобы удерживать границы и ментальную модель, и достаточно простым, чтобы им реально пользовались. |
| 76 | |
| 77 | ### Что считаем capability |
| 78 | |
| 79 | - **Service capability**: “я предоставляю сервис” (контракт/интерфейс + реализация). |
| 80 | - **Command capability**: “я предоставляю команду” (discoverability, категория, горячие клавиши, доступность). |
| 81 | - **UI surface capability**: “я предоставляю UI‑слот/поверхность” (панель/страница/вкладка), при этом включаемость/раскладка остаются overlay’ем (TOML). |
| 82 | |
| 83 | ### Code-first регистрация модулей (без MEF/рефлексии) |
| 84 | |
| 85 | - Реестр модулей формируется **явно кодом** (ручной список), без сканирования сборок. |
| 86 | - Каждая фича имеет одну точку входа регистрации, например `ICascadeFeatureModule` → `Register(ICapabilityRegistry registry)`. |
| 87 | |
| 88 | ### Ключи и зависимости |
| 89 | |
| 90 | - Capability идентифицируются **строковыми id** (константы в SDK/контрактах), например: `git.core`, `docs.markdown.exportExpanded`. |
| 91 | - В capability‑descriptor поддерживаются **явные зависимости** (`Requires`) для объяснимости (“почему capability не активна/не видна”). |
| 92 | |
| 93 | ### Capability-map (introspection) |
| 94 | |
| 95 | Регистрирующий слой обязан собирать “карту возможностей” (`CapabilityMap`) как неизменяемое описание: |
| 96 | |
| 97 | - список service/command/ui capabilities с метаданными (id, owner module, stability, tags, requires); |
| 98 | - состояние “доступно/включено” может вычисляться shell’ом с учётом overlay (например UiMode TOML) и рантайм условий. |
| 99 | |
| 100 | Capability-map используется для: |
| 101 | |
| 102 | - диагностики и объяснимости (“почему кнопка/панель отсутствует”), |
| 103 | - телеметрии и снимков UI/агента (introspection), |
| 104 | - будущего plugin-host (внешний модуль должен регистрироваться тем же способом). |
| 105 | |
| 106 | ### TOML overlay (presentation) |
| 107 | |
| 108 | UI-режимы/раскладки (TOML) должны ссылаться на capabilities **по id**, управляя presentation (видимость/размещение), но не подменяя семантику capabilities. |
| 109 | |
| 110 | --- |
| 111 | |
| 112 | ## Минимальный API (черновик контрактов) |
| 113 | |
| 114 | Ниже — минимальная форма контрактов для `CascadeIDE.Contracts` (без привязки к DI-контейнеру/фреймворку): |
| 115 | |
| 116 | - `ICascadeFeatureModule` |
| 117 | - `string Id { get; }` |
| 118 | - `void Register(ICapabilityRegistry registry)` |
| 119 | |
| 120 | - `ICapabilityRegistry` |
| 121 | - `void RegisterService(ServiceCapabilityDescriptor descriptor)` |
| 122 | - `void RegisterCommand(CommandCapabilityDescriptor descriptor)` |
| 123 | - `void RegisterUiSurface(UiSurfaceCapabilityDescriptor descriptor)` *(опционально для MVP)* |
| 124 | - `CapabilityMap BuildMap()` *(для introspection/диагностики)* |
| 125 | |
| 126 | - `CapabilityMap` |
| 127 | - `IReadOnlyList<ServiceCapabilityDescriptor> Services` |
| 128 | - `IReadOnlyList<CommandCapabilityDescriptor> Commands` |
| 129 | - `IReadOnlyList<UiSurfaceCapabilityDescriptor> UiSurfaces` |
| 130 | |
| 131 | - Общие поля дескрипторов: |
| 132 | - `string Id` (строгий идентификатор) |
| 133 | - `string OwnerModuleId` |
| 134 | - `ApiStability Stability` + `[ApiStability]` |
| 135 | - `string[] Tags` |
| 136 | - `string[] Requires` |
| 137 | |
| 138 | Пояснение: `CapabilityMap` описывает “что зарегистрировано”, а “включено ли” и “как размещено” вычисляется shell’ом с учётом TOML overlay и рантайм условий. |
| 139 | |
| 140 | --- |
| 141 | |
| 142 | ## Что НЕ является целью (v1) |
| 143 | |
| 144 | - Не вводить сейчас обязательный механизм “плагины из папки DLL”. |
| 145 | - Не обещать бинарную совместимость для сторонних расширений “навсегда”. |
| 146 | - Не превращать SDK в “бог-API” без границ (наоборот, цель — уменьшить поверхность). |
| 147 | |
| 148 | --- |
| 149 | |
| 150 | ## Практические принципы проектирования SDK |
| 151 | |
| 152 | - **Минимальная поверхность**: контракты должны описывать “что нужно”, а не “как сделано”. |
| 153 | - **Зависимости направлены наружу**: фичи зависят от контрактов, а не от конкретных фич. |
| 154 | - **DTO отдельно от UI**: переносимые модели данных не зависят от Avalonia-контролов. |
| 155 | - **Тестируемость**: контракты легко мокать; выполнение выносится в сервисы. |
| 156 | - **Безопасность по умолчанию**: всё, что запускает код/процессы/сетевые запросы — через явные шлюзы и настройки. |
| 157 | |
| 158 | --- |
| 159 | |
| 160 | ## Как отражаем `Stable`/`Experimental` в коде и документации |
| 161 | |
| 162 | Минимальный договор (без бюрократии, полезный уже в active-dev): |
| 163 | |
| 164 | - **Namespace-сигнал**: в `CascadeIDE.Contracts` два “корня”: |
| 165 | - `CascadeIDE.Contracts.Experimental.*` — по умолчанию всё новое. |
| 166 | - `CascadeIDE.Contracts.Stable.*` — то, на что команда осознанно опирается. |
| 167 | - **Атрибут-маркер**: единый `ApiStabilityAttribute` + enum (`Experimental`, `Stable`) для быстрых поисков/анализа и расширения в будущем (например `Deprecated`). |
| 168 | - **Зачем оба**: namespace даёт “видимость по умолчанию” (и упрощает правила зависимостей), атрибут даёт точечную метку и основу для будущих анализаторов/отчётов. |
| 169 | - **Документация**: в ADR и/или в README проекта контрактов фиксируем правило “по умолчанию Experimental”, критерии перевода в Stable и ожидание миграции при breaking-change. |
| 170 | |
| 171 | --- |
| 172 | |
| 173 | ## Последствия |
| 174 | |
| 175 | - Новые фичи добавляются через понятные слоты/контракты, меньше “скрытых” связей. |
| 176 | - Ускоряется разработка: проще понять куда добавить логику, проще тестировать, меньше регрессий от случайных зависимостей. |
| 177 | - Появляется ровная база для будущих плагинов: когда plugin-host перестанет быть deferred, “SDK-форма” уже будет существовать. |
| 178 | |
| 179 | --- |
| 180 | |
| 181 | ## Отклонённые альтернативы |
| 182 | |
| 183 | - “SDK = сразу plugin-host (MEF/загрузка DLL)” — отклонено как преждевременное усложнение ([0005](0005-defer-dynamic-plugins-mef.md)). |
| 184 | - “Никакого SDK, всё через прямые ссылки” — отклонено: ухудшает ментальную модель, ускоряет рост связности и размеров shell-композитора. |
| 185 | |
| 186 | --- |
| 187 | |
| 188 | ## Открытые вопросы |
| 189 | |
| 190 | ## Визуализация и документация capability-map (принятое направление) |
| 191 | |
| 192 | Минимальный путь (v1), без отдельного UI: |
| 193 | |
| 194 | - **Принцип “тонких снапшотов”** (применимо и к capability-map, и к UI snapshot’ам/прочим дампам): |
| 195 | - по умолчанию возвращаем **summary + hash** (и, при необходимости, короткий список id/счётчики); |
| 196 | - “толстые” данные получаем **по запросу** (фильтры/пагинация) или через **dump‑файл**. |
| 197 | - **JSON dump‑файл** capability-map доступен в diagnostics/логах (для объяснимости и отладки интеграций), с возвратом `path + hash`. |
| 198 | - Capability-map может быть включён в **MCP/UI snapshot** только в виде **summary**; детали — отдельной командой или через dump‑файл (чтобы не раздувать контекст). |
| 199 | |
| 200 | Следующий шаг (когда появится потребность): |
| 201 | |
| 202 | - Страница/таб **Capabilities** в Debug/Power (или отдельный документ в `docs/`) с группировкой по `OwnerModuleId` и фильтром по `Stable/Experimental`. |
| 203 | |
| 204 | |