ADR 0024: SDK для CascadeIDE — стабильные контракты для внутреннего расширения и будущих плагинов¶
Статус: Proposed
Дата: 2026-04-08
Связанные ADR¶
| ADR | Роль |
|---|---|
| 0005 | плагины deferred |
| 0006 | границы слоёв/срезов |
| 0008 | контракты и тестируемая инфраструктура |
| 0013 | поверхность команд |
| 0019 | общий core как прецедент |
| 0023 | внешние инструменты/LSP как контракты |
| 0026 | геометрия превью Markdown в TOML |
| 0025 | зоны внимания PFD/MFD/… в SDK и capabilities |
Резюме¶
- CascadeIDE SDK — стабильные контракты для внутреннего расширения и будущих плагинов.
- Версионирование публичных API; граница с «всё internal».
- Связь с зонами внимания — 0025.
Контекст¶
CascadeIDE активно развивается “изнутри”: добавляются новые панели, инструменты, каналы диагностики, режимы UI и интеграции с внешними процессами (LSP/DAP/MCP). При этом:
- Удобство разработки упирается в ментальную модель: где границы, какие зависимости допустимы, что считается “ядром”, что — “фича”.
MainWindowViewModelи UI-shell неизбежно становятся местом, куда “просится всё”, если нет явных контрактов.- Плагины (динамическая загрузка DLL/MEF) пока отложены (0005), но когда мы к ним вернёмся, нужен будет “куда вставлять”, иначе plugin-host появится раньше, чем стабилизируется форма слотов/контрактов.
Нужен “SDK” — но в первую очередь для нас же, чтобы:
- уменьшить связность между фичами,
- обеспечить расширяемость без хаоса,
- подготовить базу под будущие плагины без преждевременного plugin-host.
Решение¶
Принять подход: SDK = стабильные контракты расширения IDE, а не обязательная система плагинов сегодня.
SDK в v1 включает:
- Явные публичные интерфейсы/контракты между shell и фичами (и между фичами), оформленные как отдельный слой (папка/проект), с минимальными зависимостями.
-
Формат поставки: отдельный проект
CascadeIDE.Contracts(или близкое имя), а не папка внутриcascade-ide. -
Capability-модель (объявление возможностей), чтобы фича могла “подключаться” к shell и друг к другу без прямых ссылок на конкретные реализации.
-
Capability registry — hybrid (следует из текущей логики IDE):
- Code-first регистрация capabilities фичами при старте (без MEF/рефлексии “на вырост”).
- Data overlay для презентации: UI-режимы/раскладки (TOML) могут включать/выключать или менять presentation capabilities, но не подменяют их семантику.
-
Introspection: shell умеет собрать “карту возможностей” для диагностики/телеметрии/агента.
-
Command surface как контракт: команды (и их discoverability) оформляются не через “познавание внутренностей” фичи, а через общий контракт регистрации/метаданных (в духе 0013).
-
Out-of-proc как норма для интеграций (LSP/DAP/MCP/CLI): протоколы и DTO — часть “SDK” в смысле стабильных контрактов (см. 0008). In-proc расширения не запрещены, но должны входить в рамки контрактов.
-
Маркировка контрактов
Experimental/Stable— внутренняя ясность, не публичное обещание.
На стадии active-dev (далеко до alpha) цель — не “совместимость навсегда”, а дисциплина границ: по умолчанию всёExperimental, аStableпоявляется только там, где команда осознанно хочет опираться на контракт. ЛоматьExperimentalможно свободно; ломатьStable— осознанно (через ADR/заметку о миграции), SemVer — когда появится продуктовая потребность. -
Отложенный plugin-host: динамическая загрузка плагинов остаётся deferred (см. 0005), но будущий plugin-host обязан подключаться через те же контракты SDK, что и “внутренние” фичи.
Модель внимания кокпита (PFD / MFD / Forward / EICAS / HUD) и SDK¶
Семантика зон описана в 0021. Явная привязка UI‑capabilities к зонам внимания на уровне CascadeIDE.Contracts — отдельное решение и поэтапная реализация: 0025. Так 0024 остаётся про общий SDK, а кокпитная ось не смешивается с registry/plugin‑обсуждением.
Capability registry / capability-map (API и принципы)¶
Цель: capability-слой должен быть достаточно строгим, чтобы удерживать границы и ментальную модель, и достаточно простым, чтобы им реально пользовались.
Что считаем capability¶
- Service capability: “я предоставляю сервис” (контракт/интерфейс + реализация).
- Command capability: “я предоставляю команду” (discoverability, категория, горячие клавиши, доступность).
- UI surface capability: “я предоставляю UI‑слот/поверхность” (панель/страница/вкладка), при этом включаемость/раскладка остаются overlay’ем (TOML).
Code-first регистрация модулей (без MEF/рефлексии)¶
- Реестр модулей формируется явно кодом (ручной список), без сканирования сборок.
- Каждая фича имеет одну точку входа регистрации, например
ICascadeFeatureModule→Register(ICapabilityRegistry registry).
Ключи и зависимости¶
- Capability идентифицируются строковыми id (константы в SDK/контрактах), например:
git.core,docs.markdown.exportExpanded. - В capability‑descriptor поддерживаются явные зависимости (
Requires) для объяснимости (“почему capability не активна/не видна”).
Capability-map (introspection)¶
Регистрирующий слой обязан собирать “карту возможностей” (CapabilityMap) как неизменяемое описание:
- список service/command/ui capabilities с метаданными (id, owner module, stability, tags, requires);
- состояние “доступно/включено” может вычисляться shell’ом с учётом overlay (например UiMode TOML) и рантайм условий.
Capability-map используется для:
- диагностики и объяснимости (“почему кнопка/панель отсутствует”),
- телеметрии и снимков UI/агента (introspection),
- будущего plugin-host (внешний модуль должен регистрироваться тем же способом).
TOML overlay (presentation)¶
UI-режимы/раскладки (TOML) должны ссылаться на capabilities по id, управляя presentation (видимость/размещение), но не подменяя семантику capabilities.
Минимальный API (черновик контрактов)¶
Ниже — минимальная форма контрактов для CascadeIDE.Contracts (без привязки к DI-контейнеру/фреймворку):
ICascadeFeatureModulestring Id { get; }-
void Register(ICapabilityRegistry registry) -
ICapabilityRegistry void RegisterService(ServiceCapabilityDescriptor descriptor)void RegisterCommand(CommandCapabilityDescriptor descriptor)void RegisterUiSurface(UiSurfaceCapabilityDescriptor descriptor)(опционально для MVP)-
CapabilityMap BuildMap()(для introspection/диагностики) -
CapabilityMap IReadOnlyList<ServiceCapabilityDescriptor> ServicesIReadOnlyList<CommandCapabilityDescriptor> Commands-
IReadOnlyList<UiSurfaceCapabilityDescriptor> UiSurfaces -
Общие поля дескрипторов:
string Id(строгий идентификатор)string OwnerModuleIdApiStability Stability+[ApiStability]string[] Tagsstring[] Requires
Пояснение: CapabilityMap описывает “что зарегистрировано”, а “включено ли” и “как размещено” вычисляется shell’ом с учётом TOML overlay и рантайм условий.
Что НЕ является целью (v1)¶
- Не вводить сейчас обязательный механизм “плагины из папки DLL”.
- Не обещать бинарную совместимость для сторонних расширений “навсегда”.
- Не превращать SDK в “бог-API” без границ (наоборот, цель — уменьшить поверхность).
Практические принципы проектирования SDK¶
- Минимальная поверхность: контракты должны описывать “что нужно”, а не “как сделано”.
- Зависимости направлены наружу: фичи зависят от контрактов, а не от конкретных фич.
- DTO отдельно от UI: переносимые модели данных не зависят от Avalonia-контролов.
- Тестируемость: контракты легко мокать; выполнение выносится в сервисы.
- Безопасность по умолчанию: всё, что запускает код/процессы/сетевые запросы — через явные шлюзы и настройки.
Как отражаем Stable/Experimental в коде и документации¶
Минимальный договор (без бюрократии, полезный уже в active-dev):
- Namespace-сигнал: в
CascadeIDE.Contractsдва “корня”: CascadeIDE.Contracts.Experimental.*— по умолчанию всё новое.CascadeIDE.Contracts.Stable.*— то, на что команда осознанно опирается.- Атрибут-маркер: единый
ApiStabilityAttribute+ enum (Experimental,Stable) для быстрых поисков/анализа и расширения в будущем (напримерDeprecated). - Зачем оба: namespace даёт “видимость по умолчанию” (и упрощает правила зависимостей), атрибут даёт точечную метку и основу для будущих анализаторов/отчётов.
- Документация: в ADR и/или в README проекта контрактов фиксируем правило “по умолчанию Experimental”, критерии перевода в Stable и ожидание миграции при breaking-change.
Последствия¶
- Новые фичи добавляются через понятные слоты/контракты, меньше “скрытых” связей.
- Ускоряется разработка: проще понять куда добавить логику, проще тестировать, меньше регрессий от случайных зависимостей.
- Появляется ровная база для будущих плагинов: когда plugin-host перестанет быть deferred, “SDK-форма” уже будет существовать.
Отклонённые альтернативы¶
- “SDK = сразу plugin-host (MEF/загрузка DLL)” — отклонено как преждевременное усложнение (0005).
- “Никакого SDK, всё через прямые ссылки” — отклонено: ухудшает ментальную модель, ускоряет рост связности и размеров shell-композитора.
Открытые вопросы¶
Визуализация и документация capability-map (принятое направление)¶
Минимальный путь (v1), без отдельного UI:
- Принцип “тонких снапшотов” (применимо и к capability-map, и к UI snapshot’ам/прочим дампам):
- по умолчанию возвращаем summary + hash (и, при необходимости, короткий список id/счётчики);
- “толстые” данные получаем по запросу (фильтры/пагинация) или через dump‑файл.
- JSON dump‑файл capability-map доступен в diagnostics/логах (для объяснимости и отладки интеграций), с возвратом
path + hash. - Capability-map может быть включён в MCP/UI snapshot только в виде summary; детали — отдельной командой или через dump‑файл (чтобы не раздувать контекст).
Следующий шаг (когда появится потребность):
- Страница/таб Capabilities в Debug/Power (или отдельный документ в
docs/) с группировкой поOwnerModuleIdи фильтром поStable/Experimental.