| 1 | # ADR 0079: IDS (Ide Display System) — пайплайн оверлеев IDE, ортогонально CDS |
| 2 | |
| 3 | **Статус:** Accepted |
| 4 | **Дата:** 2026-04-20 |
| 5 | |
| 6 | ## Связанные ADR |
| 7 | |
| 8 | | ADR | Роль | |
| 9 | |-----|------| |
| 10 | | [0036](0036-cds-channel-compositor-surface-pipeline.md) | CDS: кабина, канал → CDS → композитор → поверхность | |
| 11 | | [0066](0066-cockpit-ui-vs-ide-presentation-layer.md) | Cockpit UI vs presentation IDE: хром и оверлеи | |
| 12 | | [0070](0070-command-palette-direct-overlay-surface.md) | Command Palette как прямой overlay в активном `TopLevel` | |
| 13 | | [0057](0057-chat-surface-pipeline-adoption.md) | чат: композитор снимка поверхности; аналогия по слою «композиция» | |
| 14 | | [0013](0013-command-surface-and-discoverability.md) | палитра и discoverability | |
| 15 | | [0115](0115-cds-graph-backed-shared-layer.md) | CDS — общий слой graph-backed приборов (реализация в кабине, не IDS) | |
| 16 | |
| 17 | ## Резюме |
| 18 | |
| 19 | - **IDS** — пайплайн IDE-оверлеев (intent → композитор → снимок → поверхность). |
| 20 | - Ортогонально CDS [0036](0036-cds-channel-compositor-surface-pipeline.md); Roslyn CASCOPE013–016. |
| 21 | |
| 22 | |
| 23 | --- |
| 24 | ## Контекст |
| 25 | |
| 26 | [0036](0036-cds-channel-compositor-surface-pipeline.md) задаёт цепочку для **семантики кабины** (PFD/MFD/инструменты, `CockpitSurfaceState`, слоты приборов). Она **не должна** разрастаться до «всех пикселей IDE», иначе CDS станет God-слоем и смешается с [0066](0066-cockpit-ui-vs-ide-presentation-layer.md). |
| 27 | |
| 28 | Параллельно продукту нужны **глобальные IDE-оверлеи**: командная палитра (`Ctrl+Q`), позже — toast, блокирующие диалоги, иные «поверх всего workspace» поверхности. У них другие инварианты: |
| 29 | |
| 30 | - привязка к **активному `TopLevel`** и возврат фокуса ([0070](0070-command-palette-direct-overlay-surface.md)); |
| 31 | - **keyboard-first** и предсказуемый перехват ввода; |
| 32 | - по возможности — **снимки** для тестов и наблюдаемости агента без привязки к конкретному дереву `Control`. |
| 33 | |
| 34 | Без явного имени и границ этот контур расползается по `Views/*` и `*ViewModel`, дублируя логику «кто сверху» и «кто ест клавиши». |
| 35 | |
| 36 | --- |
| 37 | |
| 38 | ## Решение |
| 39 | |
| 40 | Вводится отдельный продуктовый контур **IDS — Ide Display System** (неймспейс и типы в коде: `CascadeIDE.IdeDisplay.*`). |
| 41 | |
| 42 | ### 1. Назначение IDS |
| 43 | |
| 44 | **IDS** описывает **presentation IDE** для **модальных и полумодальных оверлеев** (и в перспективе — других IDE-surfaces, которые не являются приборами кабины): отделение **данных/намерения** и **композиции снимка** от конкретного рендера (Avalonia `Control`, Skia-хост, гибрид). |
| 45 | |
| 46 | **CDS** остаётся каноном **кабины**; **IDS** — канон **оверлеев IDE**. Смешивать семантику слотов кабины и стек IDE-оверлеев в одном типе — анти-паттерн; при необходимости общ only на уровне слов «канал / композитор / снимок», не на уровне одного DTO. |
| 47 | |
| 48 | <a id="adr0079-cds-vs-ids"></a> |
| 49 | |
| 50 | ### Различение CDS и IDS (одно приложение, два домена) |
| 51 | |
| 52 | Оба контура выполняются **внутри одного процесса** CascadeIDE (географически «всё это IDE»). Путаница снимается вопросом **домена смысла**, а не exe-файла: |
| 53 | |
| 54 | | | **CDS (и кабина вокруг него)** | **IDS** | |
| 55 | | --- | --- | --- | |
| 56 | | **Вопрос** | Куда в **модели внимания кабины** попадает содержимое: PFD / Forward / MFD, слот прибора, страница вторичного контура? | Как показать **оверлей оболочки** поверх workspace: палитра, модалка, toast; фокус, z-order, перехват ввода? | |
| 57 | | **Типичный тест** | «На какой странице MFD / в какой зоне deck / в каком слоте прибора?» | «Модально поверх всего окна, как в обычном приложении, без семантики прибора?» | |
| 58 | | **Норматив** | [0036](0036-cds-channel-compositor-surface-pipeline.md), [0021](0021-pfd-mfd-cockpit-attention-model.md) | этот ADR, [0070](0070-command-palette-direct-overlay-surface.md) | |
| 59 | | **Слои «прибор vs хром»** | Семантика **приборов и зон** ([0036](0036-cds-channel-compositor-surface-pipeline.md)) | **Не-приборный** presentation shell ([0066](0066-cockpit-ui-vs-ide-presentation-layer.md)) | |
| 60 | |
| 61 | **Мнемоника:** решаешь «**куда в кабине**» → **CDS**; «**глобально поверх workspace**, не слот прибора» → **IDS**. Границу закрепляет **CASCOPE016**: `Cockpit/` не импортирует `IdeDisplay`. |
| 62 | |
| 63 | ### 2. Та же дисциплина цепочки, что у 0036 (по смыслу, не по типам) |
| 64 | |
| 65 | Для каждого оверлея в IDS соблюдается цепочка: |
| 66 | |
| 67 | 1. **Канал / состояние** — что пользователь ввёл, что отфильтровано, какой режим (команды / go-to / melody), без координат пикселей. |
| 68 | 2. **Композитор поверхности** — чистая функция (или близко): из канала строит **снимок** для рендера и навигации (строки, выделение, подсказки, opacity). |
| 69 | 3. **Поверхность** — Avalonia/Skia: фокус, hit-test, анимации; не источник семантики оверлея. |
| 70 | |
| 71 | Обобщающий контракт композиторов: `IIdsSurfaceCompositor<TIntent, TSnapshot>` с методом `TSnapshot Compose(TIntent intent)` (параметр **без** `in` при контравариантном `TIntent` — ограничение языка C#). |
| 72 | |
| 73 | ### 3. Единый хост ввода (input capture) |
| 74 | |
| 75 | Инвариант **направления**: перехват клавиатуры/фокуса для **стека оверлеев IDE** сходится к **одному месту** — условный **`IdsOverlayHost`** / **input router** на корне активного host, а не к разрозненным `PreviewKeyDown` в каждом оверлее без политики. |
| 76 | |
| 77 | На момент принятия ADR полная реализация хоста может быть **strangler**; палитра может временно оставаться self-contained ([0070](0070-command-palette-direct-overlay-surface.md)), но **новые** оверлеи не должны добавлять третий независимый «глобальный перехват» без явного согласования в этом ADR или отдельном ADR. |
| 78 | |
| 79 | ### 4. Слоты (`SurfaceSlot`) — опционально |
| 80 | |
| 81 | **`SurfaceSlot` (или `IdsSurfaceSlot`)** вводится, когда появляется **несколько** одновременно конкурирующих IDS-поверхностей или нужен явный **z-order / mount id** для наблюдаемости и тестов. |
| 82 | |
| 83 | Для **одной** палитры как единственного верхнего оверлея слот не обязателен. Для «палитра + toast + блокирующий диалог» — слот или эквивалентный **стек** становится частью IDS-хоста. |
| 84 | |
| 85 | Именование: по умолчанию **`Ids*`** в коде, чтобы не путать с **`CockpitInstrumentDescriptor`** и слотами CDS ([0047](0047-cockpit-instrument-descriptor-and-slot-composition.md)). |
| 86 | |
| 87 | ### 5. Наблюдаемость агента |
| 88 | |
| 89 | Снимки IDS (например, состав строк палитры и выбранный индекс) могут поставляться в MCP/контракт агента **отдельно** от `cockpit_surface`, либо тонким полем «overlay» в общей сводке — решение по полю JSON не фиксирует этот ADR; фиксируется только **разделение семантики** CDS vs IDS. |
| 90 | |
| 91 | ### 6. Roslyn-guardrails (CASCOPE013–016) |
| 92 | |
| 93 | На сборке [`CascadeIDE.ArchitectureAnalyzers`](../../CascadeIDE.ArchitectureAnalyzers/README.md): |
| 94 | |
| 95 | - **CASCOPE013** / **CASCOPE014** / **CASCOPE015** — в каталоге `IdeDisplay/` запрещены зависимости от `CascadeIDE.Cockpit…`, от Avalonia UI и от `CascadeIDE.Features.UiChrome` (в т.ч. типы в членах классов в `CascadeIDE.IdeDisplay.*`). |
| 96 | - **CASCOPE016** — в `Cockpit/` запрещён `using CascadeIDE.IdeDisplay…` (кабина не тянет IDS). |
| 97 | |
| 98 | Полные формулировки и уровни — в README анализаторов. |
| 99 | |
| 100 | --- |
| 101 | |
| 102 | ## Состояние реализации (фиксация на дату ADR) |
| 103 | |
| 104 | - **Сделано:** контракт `IIdsSurfaceCompositor<TIntent, TSnapshot>`; для палитры — `CommandPaletteSurfaceIntent`, `CommandPaletteSurfaceSnapshot`, `CommandPaletteSurfaceCompositor`; обновление снимка из `MainWindowViewModel` при открытой палитре; тесты композитора; Roslyn **CASCOPE013–016** (см. §6). |
| 105 | - **По плану (strangler):** единый `IdsOverlayHost` / router ввода; опциональные `IdsSurfaceSlot` при втором оверлее; при необходимости — проекция в MCP без смешения с CDS. |
| 106 | |
| 107 | --- |
| 108 | |
| 109 | ## Последствия |
| 110 | |
| 111 | - Новые IDE-оверлеи по умолчанию проектируются через **IDS** (intent → compositor → snapshot → surface), а не «сразу в AXAML логикой наполовину в VM». |
| 112 | - CDS и реестр приборов кабины **не раздуваются** ответственностью за глобальные IDE-модалки. |
| 113 | - Тесты могут опираться на **снимок IDS** там, где важна семантика списка/выбора, без хрупкой привязки к визуальному дереву. |
| 114 | |
| 115 | ## Не цели |
| 116 | |
| 117 | - Замена Avalonia как host для оверлеев. |
| 118 | - Переименование или перепрофилирование **CDS** под IDE-chrome. |
| 119 | - Полная спецификация JSON для агента в этом ADR. |
| 120 | |
| 121 | ## Отклонённые альтернативы (кратко) |
| 122 | |
| 123 | - **Всё в CDS:** отклонено — смешивает кабину и IDE-shell, ломает [0066](0066-cockpit-ui-vs-ide-presentation-layer.md). |
| 124 | - **Отдельный ADR на каждый оверлей без общего IDS-имени:** допустимо как временная мера, но ведёт к дублированию политики input/stack. |
| 125 | |