Forge
markdowndeeb25a2
1<!-- English translation of adr/0024-ide-sdk-and-stable-contracts.md. Canonical Russian: ../../adr/0024-ide-sdk-and-stable-contracts.md -->
2
3# ADR 0024: SDK for CascadeIDE - stable contracts for internal extension and future plugins
4
5**Status:** Proposed
6**Date:** 2026-04-08
7
8## Related ADRs
9
10| ADR | Role |
11|-----|------|
12| [0005](0005-defer-dynamic-plugins-mef.md) | deferred plugins |
13| [0006](0006-presentation-layers-and-feature-slices.md) | layer/slice boundaries |
14| [0008](0008-mcp-contracts-and-testable-infrastructure.md) | contracts and testing infrastructure |
15| [0013](0013-command-surface-and-discoverability.md) | command surface |
16| [0019](0019-shared-git-core-ide-and-git-mcp.md) | common core as a precedent |
17| [0023](0023-markdown-diagrams-language-tooling.md) | external instruments/LSPs as contracts |
18| [0026](0026-markdown-preview-surfaces-and-placement.md) | geometry preview Markdown in TOML |
19| [0025](0025-sdk-attention-zones-and-capabilities.md) | zones of attention PFD/MFD/… in SDK and capabilities |
20
21## Summary
22
23- **CascadeIDE SDK** - stable contracts for internal extension and future plugins.
24- Versioning of public APIs; border with “everything internal”.
25- Communication with attention zones - [0025](0025-sdk-attention-zones-and-capabilities.md).
26
27---
28## Context
29
30CascadeIDE is actively developing “from the inside”: new panels, tools, diagnostic channels, UI modes and integration with external processes (LSP/DAP/MCP) are being added. In this case:
31
32- Ease of development depends on the **mental model**: where are the boundaries, what dependencies are acceptable, what is considered “core”, what is a “feature”.
33- `MainWindowViewModel` and UI-shell inevitably become a place where “everything is asked” if there are no explicit contracts.
34- Plugins (dynamic loading of DLL/MEF) are **deferred** for now ([0005](0005-defer-dynamic-plugins-mef.md)), but when we return to them, we will need “where to insert”, otherwise the plugin-host will appear before the form of slots/contracts is stabilized.
35
36We need an “SDK” - but first of all **for us**, so that:
37
38- reduce coupling between features,
39- ensure extensibility without chaos,
40- prepare the base for future plugins without premature plugin-host.
41
42---
43
44## Solution
45
46Adopt the approach: **SDK = stable IDE extension contracts** rather than a must-have plugin system today.
47
48SDK v1 includes:
49
501. **Explicit public interfaces/contracts** between shell and features (and between features), designed as a separate layer (folder/project), with minimal dependencies.
51 - Delivery format: **separate project** `CascadeIDE.Contracts` (or a similar name), not a folder inside `cascade-ide`.
52
532. **Capability model** (declaration of capabilities), so that a feature can “connect” to the shell and to each other without direct references to specific implementations.
54
553. **Capability registry - hybrid (follows from the current IDE logic):**
56 - **Code-first registration** capabilities features at startup (without MEF/reflection “for growth”).
57 - **Data overlay for presentation**: UI modes/layouts (TOML) can enable/disable or change presentation capabilities, but do not replace their semantics.
58 - **Introspection**: shell can assemble a “capability map” for diagnostics/telemetry/agent.
59
604. **Command surface as a contract**: commands (and their discoverability) are formalized not through “knowing the insides” of a feature, but through a general registration/metadata contract (in the spirit of [0013](0013-command-surface-and-discoverability.md)).
61
625. **Out-of-proc as the norm for integrations** (LSP/DAP/MCP/CLI): protocols and DTOs are part of the “SDK” in the sense of stable contracts (see [0008](0008-mcp-contracts-and-testable-infrastructure.md)). In-proc extensions are not prohibited, but must be within the scope of contracts.
63
646. **Contract marking `Experimental`/`Stable` is for internal clarity, not a public promise.**
65 At the active-dev stage (far before alpha), the goal is not “forever compatibility”, but discipline of boundaries: by default everything is `Experimental`, and `Stable` appears only where the team consciously wants to rely on the contract. You can break `Experimental` freely; break `Stable` - consciously (via ADR/migration note), SemVer - when a product need appears.
66
677. **Deferred plugin-host**: dynamic loading of plugins remains deferred (see [0005](0005-defer-dynamic-plugins-mef.md)), but the future plugin-host must connect through the same SDK contracts as the “internal” features.
68
69### Cockpit Attention Model (PFD/MFD/Forward/EICAS/HUD) and SDK
70The semantics of zones is described in [0021](0021-pfd-mfd-cockpit-attention-model.md). **Explicit binding of UI‑capabilities to attention zones at the `CascadeIDE.Contracts` level** is a separate solution and phased implementation: [0025](0025-sdk-attention-zones-and-capabilities.md). So [0024](0024-ide-sdk-and-stable-contracts.md) remains about the general SDK, and the cockpit axis is not mixed with the registry/plugin discussion.
71
72---
73
74## Capability registry / capability-map (API and principles)
75
76Goal: The capability layer should be strong enough to hold boundaries and a mental model, but simple enough to actually be used.
77
78### What we consider capability
79
80- **Service capability**: “I provide a service” (contract/interface + implementation).
81- **Command capability**: “I provide a command” (discoverability, category, hotkeys, availability).
82- **UI surface capability**: “I provide a UI slot/surface” (panel/page/tab), while the inclusion/layout remains overlay (TOML).
83
84### Code-first module registration (no MEF/reflection)
85
86- The register of modules is formed **explicitly by code** (manual list), without scanning assemblies.
87- Each feature has one registration entry point, for example `ICascadeFeatureModule` → `Register(ICapabilityRegistry registry)`.
88
89### Keys and dependencies
90
91- Capabilities are identified by **string ids** (constants in the SDK/contracts), for example: `git.core`, `docs.markdown.exportExpanded`.
92- The capability-descriptor supports **explicit dependencies** (`Requires`) for explainability (“why the capability is not active/visible”).
93
94### Capability-map (introspection)
95
96The recording layer is required to collect a “capability map” as an immutable description:
97
98- list of service/command/ui capabilities with metadata (id, owner module, stability, tags, requires);
99- the “available/enabled” state can be calculated by the shell taking into account overlay (for example UiMode TOML) and runtime conditions.
100
101Capability-map is used for:
102
103- diagnostics and explainability (“why the button/panel is missing”),
104- telemetry and UI/agent snapshots (introspection),
105- future plugin-host (the external module must be registered in the same way).
106
107### TOML overlay (presentation)
108
109UI modes/layouts (TOML) must refer to capabilities **by id**, controlling presentation (visibility/placement), but not replacing the semantics of capabilities.
110
111---
112
113## Minimum API (draft contracts)
114
115Below is the minimum form of contracts for `CascadeIDE.Contracts` (without being tied to a DI container/framework):
116
117- `ICascadeFeatureModule`
118 - `string Id { get; }`
119 - `void Register(ICapabilityRegistry registry)`
120
121- `ICapabilityRegistry`
122 - `void RegisterService(ServiceCapabilityDescriptor descriptor)`
123 - `void RegisterCommand(CommandCapabilityDescriptor descriptor)`
124 - `void RegisterUiSurface(UiSurfaceCapabilityDescriptor descriptor)` *(optional for MVP)*
125 - `CapabilityMap BuildMap()` *(for introspection/diagnostics)*
126
127- `CapabilityMap`
128 - `IReadOnlyList<ServiceCapabilityDescriptor> Services`
129 - `IReadOnlyList<CommandCapabilityDescriptor> Commands`
130 - `IReadOnlyList<UiSurfaceCapabilityDescriptor> UiSurfaces`
131
132- General descriptor fields:
133 - `string Id` (strict identifier)
134 - `string OwnerModuleId`
135 - `ApiStability Stability` + `[ApiStability]`
136 - `string[] Tags`
137 - `string[] Requires`
138
139Explanation: `CapabilityMap` describes “what is registered”, and “whether it is enabled” and “how it is allocated” is calculated by the shell taking into account TOML overlay and runtime conditions.
140
141---
142
143## What is NOT the goal (v1)
144
145- Do not introduce the mandatory “plugins from DLL folder” mechanism now.
146- Do not promise binary compatibility for third-party extensions “forever”.
147- Do not turn the SDK into a “god API” without boundaries (on the contrary, the goal is to reduce the surface).
148
149---
150
151## Practical principles for SDK design
152
153- **Minimal surface**: contracts should describe “what is needed”, not “how it is done.”
154- **Dependencies are directed outwards**: features depend on contracts, not on specific features.
155- **DTO is separate from UI**: portable data models do not depend on Avalonia controls.
156- **Testability**: contracts are easy to mock; execution is transferred to services.
157- **Security by default**: everything that runs code/processes/network requests is through explicit gateways and settings.
158
159---
160
161## How to reflect `Stable`/`Experimental` in code and documentation
162
163Minimum contract (no bureaucracy, useful already in active-dev):
164- **Namespace signal**: there are two “roots” in `CascadeIDE.Contracts`:
165 - `CascadeIDE.Contracts.Experimental.*` - everything is new by default.
166 - `CascadeIDE.Contracts.Stable.*` is what the team consciously relies on.
167- **Marker attribute**: single `ApiStabilityAttribute` + enum (`Experimental`, `Stable`) for quick searches/analysis and future expansion (for example `Deprecated`).
168- **Why both**: namespace gives “default visibility” (and simplifies dependency rules), the attribute gives a point label and a basis for future analyzers/reports.
169- **Documentation**: in the ADR and/or in the README of the contract project, we fix the rule “by default Experimental”, the criteria for transferring to Stable and the expectation of migration during breaking-change.
170
171---
172
173## Consequences
174
175- New features are added through clear slots/contracts, fewer “hidden” connections.
176- Development is accelerated: it’s easier to understand where to add logic, easier to test, fewer regressions from random dependencies.
177- A smooth base appears for future plugins: when the plugin-host ceases to be deferred, the “SDK form” will already exist.
178
179---
180
181## Rejected alternatives
182
183- “SDK = immediately plugin-host (MEF/DLL loading)” - rejected as premature complication ([0005](0005-defer-dynamic-plugins-mef.md)).
184- “No SDK, everything through direct links” - rejected: worsens the mental model, accelerates the growth of connectivity and size of the shell composer.
185
186---
187
188## Open questions
189
190## Visualization and documentation capability-map (accepted direction)
191
192Minimal path (v1), without separate UI:
193
194- **The principle of “thin snapshots”** (applicable to both capability-maps and UI snapshots/other dumps):
195 - by default we return **summary + hash** (and, if necessary, a short list of ids/counters);
196 - “thick” data is obtained **by request** (filters/pagination) or through a **dump file**.
197- **JSON dump file** capability-map is available in diagnostics/logs (for explainability and debugging of integrations), returning `path + hash`.
198- Capability-map can be included in **MCP/UI snapshot** only in **summary** form; details - with a separate command or through a dump file (so as not to inflate the context).
199
200Next step (when needed):
201
202- **Capabilities** page/tab in Debug/Power (or a separate document in `docs/`) with grouping by `OwnerModuleId` and filter by `Stable/Experimental`.
View only · write via MCP/CIDE