Forge

docs/ / adr/0163-monaco-native-capability-bus-full-forward-migration.md · branch develop

ADR 0163: Monaco-native capability bus и полный перевод Forward-редактора

Статус: Accepted (M7–M10 implemented on feature/monaco-forward-editor-m0; M11 deferred)
Дата: 2026-06-21
Заменяет / уточняет: 0162 §2.1 (dual-path avalonia_edit), §5 (фазы M6 «optional default») — направление: единственный Forward-хост = Monaco; AvaloniaEdit выводится из Forward, не из всего CIDE.
Дополняет: 0103, 0085, LANGUAGE-SERVICES-PLAN.md

Связанные ADR

ADR Роль
0162 WebView2 island, cide-editor/* v0, IEditorSurfaceAdapter, strangler M0–M6
0164 Presentation projection, line-first push-DTO, DecorationLayerManager, dock chrome
0103 Субстрат HUD, hi-freq bounded-контур, адаптер поверхности
0084 Versioned buffer, applyEdits
0085 Inline vs HUD banner
0152 CF gutter / virtual spacing
0130 Agent range reveal
0128 CodeAnchor / AttachmentAnchor — file + line range + member
0137 L4 discourse ↔ code; gutter infer
0155 Correspondence L0–L4, drift
0156 CRS MFD; reverse anchors → CodeAnchor
0157 cide:// → IDE navigation
0039 Карта намерений; graph → reveal
0040 LSP presets, OmniSharp
0009 Strangler, удаление legacy после parity

Вне ADR

Документ Роль
editor-hud-inline-migration-inventory-v1 Таблица Avalonia adorners → целевой владелец
LANGUAGE-SERVICES-PLAN.md Roslyn in-process + LSP hybrid
Assets/cide-editor/ Bundled Monaco + bridge v1

1. Контекст

1.1 Что мы костыляли в AvaloniaEdit

В Forward-редакторе на AvaloniaEdit значительная часть «IDE-ощущения» была самописной презентацией, а не API редактора:

Область Avalonia-подход Боль
Squiggles / волны IBackgroundRenderer Z-order, redraw, hit-test вручную
Completion / signature EditorIntelligence + Avalonia Popup Не VS Code UX; дублирование LSP
Hover / Quick Info ToolTip на TextEditor + debounce Placement, race с кареткой
Reference highlights Отдельный renderer Нет связи с selection API
Inlay hints (var → type) EOL renderer (AvaloniaEdit #429) Нет intra-line inlays
Breakpoints / debug line IBackgroundRenderer + margin hit-test Дублирование с DAP UI
Agent reveal EditorAgentRangeReveal renderer Отдельный lifecycle
CF gutter Custom spacing generator + glyph renderer Layout-хак
Подсветка синтаксиса TextMate через AvaloniaEdit Два стека тем (Avalonia + TM)

0103 правильно отделил субстрат (EditorHudEngine, SemanticProjectionPipeline, DAL-полосы) от поверхности. Но реализация поверхности на AvaloniaEdit оставалась дорогой и неполной.

1.2 Что даёт Monaco «из коробки»

Monaco Editor (тот же движок, что под VS Code) уже предоставляет:

  • monaco.editor.create + ITextModel + versioned edits
  • deltaDecorations — слоистые decoration sets (аналог нашего setId)
  • registerCompletionItemProvider, registerHoverProvider, registerSignatureHelpProvider
  • registerDefinitionProvider, registerReferenceProvider, registerRenameProvider
  • registerCodeLensProvider, registerInlayHintsProvider
  • registerDocumentSemanticTokensProvider (LSP semantic highlighting)
  • Built-in minimap, sticky scroll (опция), glyph margin, folding
  • Monarch — декларативные грамматики для десятков языков в vs/basic-languages
  • Diff editor, multi-cursor, accessibility — без Avalonia adorners

Мы не обязаны переносить Avalonia-рендереры 1:1; цель — подключить те же capability-бэкенды CIDE к нативным Monaco provider API.

1.3 Текущее состояние (ветка feature/monaco-forward-editor-m0)

  • Forward dock: только MonacoEditorHostControl (Avalonia TextEditor удалён).
  • Bridge v1: setModel, applyEdits, setDecorations, LSP-like request/response для completion/hover/signature, CF glyphs, debug/breakpoint overlays, agent reveal, epoch dim.
  • Roslyn fast path: CSharpLanguageService из C# по editor/requestCompletion и т.д.
  • Проблема v1: bridge — плоский список сообщений, провайдеры регистрируются вручную в cide-editor-bridge.js; нет единой шины capabilities, нет LSP как первого класса, TextMate/Monarch стратегия не зафиксирована.

1.4 Зачем отдельный ADR от 0162

0162 фиксировал опциональный dual-path и осторожный strangler. Практика M0–M3+ показала:

  1. Parity по UX быстрее через Monaco API, чем через доводку Avalonia adorners.
  2. Поддерживать два forward-хоста — двойная стоимость (inventory в editor-hud-inline-migration-inventory-v1).
  3. Нужен явный контракт между CIDE backend и Monaco — не разрастающийся switch в bridge.js.

Этот ADR фиксирует полный перевод Forward и архитектуру CIDE Editor Capability Bus.


2. Решение

2.1 Направление продукта

  1. Forward-редактор = Monaco + WebView2 — единственный поддерживаемый хост для документов в DocumentsDockView.

  2. [editor].forward_host = "monaco_webview2" — единственное значение в production; avalonia_edit deprecated, удаляется после sign-off чеклиста §6.

  3. AvaloniaEdit остаётся в репозитории только как:

    • legacy тесты / strangler artifacts до удаления;
    • не как runtime path Forward.
  4. Субстрат 0103 не меняется: EditorHudEngine, WorkspaceDiagnosticsCoordinator, LSP hosts, agent MCP — по-прежнему в C#; меняется только адаптер презентации (MonacoWebViewSurfaceAdapter + bus).

2.2 CIDE Editor Capability Bus (CECB)

Вводим шину capabilities между C# backend и Monaco host — ортогонально DataBus 0099 (editor hi-freq не в DataBus, см. 0103).

flowchart TB
  subgraph csharp ["C# — CIDE backend"]
    VM["MainWindowViewModel / Documents"]
    HUD["EditorHudEngine / SemanticProjection"]
    ROS["CSharpLanguageService — fast path"]
    LSP["CSharpLspClient — solution path"]
    DBG["Debug / Breakpoints / DAP"]
    NAV["WorkspaceNavigationMap / CF"]
    CRS["Correspondence / CodeAnchor resolve"]
    AGT["Agent MCP / applyEdits"]
    CAP["ICideEditorCapabilityRouter"]
    VM --> CAP
    HUD --> CAP
    ROS --> CAP
    LSP --> CAP
    DBG --> CAP
    NAV --> CAP
    CRS --> CAP
    AGT --> CAP
  end

  subgraph bus ["CECB — transport"]
    HOST["MonacoEditorHostControl"]
    PROTO["cide-editor/* JSON + requestId"]
    HOST --> PROTO
  end

  subgraph js ["cide-editor-host — TS"]
    REG["MonacoProviderRegistry"]
    DECO["DecorationLayerManager"]
    LANG["LanguageRegistry — Monarch / builtins"]
    REG --> MON["monaco.editor / languages.*"]
    DECO --> MON
    LANG --> MON
  end

  CAP --> HOST
  PROTO --> REG
  PROTO --> DECO
  PROTO --> LANG

Принципы bus:

Принцип Смысл
Capability-oriented Сообщения именуются по способности: capability/completion, capability/hover, capability/codeLens, capability/semanticTokens, capability/decorations, … — не по внутреннему классу C#
Request / response Async: requestId, timeout, cancel (как сейчас completion/hover)
Push / subscribe Decorations, theme, model, debug overlay — push с setId и version guard
Router в C# ICideEditorCapabilityRouter: для .cs + LSP ready → LSP; иначе Roslyn; diagnostics — всегда из WorkspaceDiagnosticsCoordinator snapshot
Thin JS cide-editor-bridge.js только: transport, registry, correlation — без бизнес-логики Roslyn
Versioned document 0084: modelVersion на все mutating push; reject on mismatch

Не дублировать LSP в кастомных сообщениях: если capability есть в LSP 3.17 — bus проксирует к CSharpLspClient, Monaco provider делает connection.sendRequest. Кастомные editor/requestCompletionвременный fast path до полного LSP wiring (strangler).

2.3 Маппинг: Avalonia-костыль → Monaco-native

Inventory (migration doc) Monaco API Bus / setId
EditorDiagnosticBackgroundRenderer deltaDecorations decorations/diagnostics
EditorIntelligence completion registerCompletionItemProvider capability/completion
EditorIntelligence signature registerSignatureHelpProvider capability/signatureHelp
Inline hover / Quick Info registerHoverProvider capability/hover
Reference highlights deltaDecorations + optional registerDocumentHighlightProvider decorations/highlights
Inlay hints registerInlayHintsProvider capability/inlayHints
Breakpoints / debug line glyphMargin + deltaDecorations isWholeLine decorations/breakpoints, decorations/debugLine
Agent reveal 0130 deltaDecorations + revealRangeInCenter decorations/agentReveal
CF gutter 0152 glyphMarginClassName + optional lineHeight decoration decorations/cfGutter
Epoch verify dim CSS overlay on container editor/setEpochDim
HUD banner (file-level) Остаётся Avalonia chrome над WebView (0085) C# StickyScrollHost + optional Monaco stickyScroll
TextMate syntax Monarch / built-in languages (§2.4) language/register

2.4 Correspondence, CodeAnchor и навигация — граница bus

Correspondence (L0–L4), reverse anchors, CRS (0155, 0156) — это домен и поверхности MFD/PFD, не Monaco. На bus попадает только итог навигации в буфер после резолва якоря.

flowchart LR
  subgraph resolve ["C# — resolve (не bus)"]
    MCP["MCP reveal_editor_range / go_to_position"]
    MAP["Navigation map graph click"]
    CRS_UI["CRS reverse anchor · open"]
    MD["Markdown cascade-code-anchor:"]
    IC["Intercom reveal_attachment"]
    CHAT["Chat bracket draft"]
    FRG["Forge codeBracket"]
    ML["cide:// magic link"]
    RES["EditorNavigationResolver\nAttachmentAnchor · Roslyn · bracket"]
    MCP --> RES
    MAP --> RES
    CRS_UI --> RES
    MD --> RES
    IC --> RES
    CHAT --> RES
    FRG --> RES
    ML --> RES
  end

  subgraph busnav ["CECB — presentation"]
    NAV["capability/navigate"]
    NAV --> REV["revealRangeInCenter"]
    NAV --> SEL["setSelectionByOffset"]
    NAV --> DEC["decorations/agentReveal"]
  end

  RES -->|"EditorNavigationTarget"| NAV

EditorNavigationTarget (C#, sketch): filePath, startLine, endLine, startColumn?, memberKey?, presentation (RevealTransient | RevealPersistent | SelectAndReveal | ScrollOnly), durationMs?, source (audit: mcp, navigation_map, crs, intercom, markdown, chat_draft, forge).

Источник ADR Режим на Monaco
reveal_editor_range 0130 RevealTransient + decorations/agentReveal
go_to_position / /editor line select 0124 SelectAndReveal
Клик узла карты намерений / CF 0039 SelectAndReveal или RevealTransient
CRS «open» reverse anchor 0156 open file + SelectAndReveal
cascade-code-anchor: в Markdown preview 0156 §2.4 SelectAndReveal
Intercom reveal_attachment 0128 как 0130
Chat bracket ghost (composer) 0128 · AnchorDraftPreviewCoordinator RevealPersistent
Forge artifact codeBracket 0159 SelectAndReveal
member_key / syntax_scope в MCP 0130 фаза 2 resolve в C# (AttachmentAnchorRoslynResolver), затем тот же capability/navigate

Единая точка входа в C#: IEditorNavigationService.NavigateAsync(EditorNavigationTarget) → open/activate document → ICideEditorCapabilityRouter.PushNavigate(...) → Monaco. Сегодня разрозненные RevealEditorRangeInDock, GoToPosition, EditorAgentRangeReveal сходятся сюда (strangler M7).

Что остаётся вне Monaco:

  • get_correspondence_context, бейдж L0–L4 на PFD, страница CRS — VM / Skia / Markdown, без WebView.
  • Индексация reverse anchors (WorkspaceCorrespondenceCodeAnchorsLoader, Forge Lens 0158) — workspace layer.
  • Intercom L4 gutter infer (0137) — event log; reveal по клику — через тот же Navigate.

Будущее (M9+): registerCodeLensProvider — линзы «ADR §…», «reverse: design/foo.md» на текущей строке; клик → EditorNavigationTarget с source=codelens_correspondence. Данные от 0155 / CRS, не дублировать парсинг в JS.

2.5 Языки: Monarch, built-ins, LSP

Стратегия (слои):

  1. Built-in Monaco languagescsharp, json, markdown, xml, yaml, powershell, typescript, … — из vendored monaco/min/vs; languageId из CideEditorLanguageIds.FromFilePath (уже есть).
  2. Monarch для кастомных — TOML workspace grammar, .axaml как XML-подмножество, domain DSL — декларативные *.monarch.ts в Assets/cide-editor/lang/, регистрация через bus language/registerMonarch.
  3. Не тащить TextMate runtime в Forward — C# pipeline RegistryOptions / EnsureTextMateOnEditor не используется для Monaco host (inventory §TextMate).
  4. Семантическая подсветка .cs — при активном LSP: textDocument/semanticTokensregisterDocumentSemanticTokensProvider; синтаксис остаётся Monarch/builtin, семантика — LSP.
  5. Конвертация TM → Monarch — одноразовый tooling при необходимости (не hot path); предпочтение: builtin > hand-written Monarch > TM bridge (monaco-textmate — spike only, тяжёлый bundle).

CodeLens: registerCodeLensProvider — источники: navigation map anchors, test run lenses (future), Roslyn — только через LSP textDocument/codeLens когда доступен.

2.6 CodeLens, DeltaDecorations, providers — единая дисциплина

DecorationLayerManager (JS):

  • Фиксированный набор setId (enum в shared manifest, C# + TS):
    • diagnostics, highlights, breakpoints, debugLine, agentReveal, cfGutter, agentEpoch (optional)
  • Один вызов editor.deltaDecorations per setId — не смешивать слои.
  • C# mapper per concern: MonacoEditorDiagnosticsMapper, MonacoEditorDebugMapper, … → тонкие, без UI логики.
  • Presentation projection: MonacoEditorPresentationProjector — единая сборка HUD push; контракт line-first — 0164.

ProviderRegistry (JS):

  • При editor/ready регистрирует providers один раз.
  • Каждый provider → postToHost({ type: 'capability/...', requestId, ... }) → C# router.

2.7 IPC (наследие 0162 §3)

Без изменений по честности: WebView2 out-of-process, throttle hi-freq (0103). Bus не обещает in-process latency; coalesce caret/selection 16–50 ms.

Origin: https://cide-editor.local/ virtual host (0162 §4).


3. Фазы (продолжение 0162 §5)

Фаза Объём Критерий
M7 Formalize CECB: ICideEditorCapabilityRouter, IEditorNavigationService, manifest setId, refactor bridge v1 → capability/* Нет новых raw editor/requestX без записи в manifest
M8 LSP-first для .cs в solution: completion, hover, definition, diagnostics, semantic tokens через LSP; Roslyn — fallback LANGUAGE-SERVICES-PLAN.md этапы 2–4
M9 Monarch pack + убрать TextMate из Forward; inlay hints + CodeLens (correspondence / test lenses) Все расширения из EditorLanguageSupport имеют languageId
M10 CF virtual spacing parity 0152; удалить avalonia_edit, dead Avalonia editor services CI без forward Avalonia path; ADR 0162 → Superseded by 0163
M11 Theme code gen 0086 → Monaco defineTheme Один источник токенов

Текущая ветка закрывает пред-M7 smoke (Monaco-only dock, bridge v1, Roslyn providers, debug/breakpoints/reveal). M7+ — структурный рефакторинг, не блокер для ручной оценки «подойдёт ли Monaco».


4. Чеклист sign-off «Monaco вместо Avalonia Forward»

Перед M10 (удаление avalonia_edit). Автотесты: dotnet test --filter Category=MonacoForward (см. CascadeIDE.Tests/MonacoForward/).

Пункт Автотест Ручной smoke
Completion / hover / signature — .cs с solution и без CideEditorCapabilityRouterTests (+ Roslyn: CSharpLanguageServiceQuickInfoTests) LSP live + solution open
Diagnostics squiggles + Problems panel sync MonacoEditorPresentationProjectorTests, HitTestForToolTip Problems UI sync
Go to definition / references (LSP или Roslyn) CideEditorCapabilityRouterTests (definition); references — нет capability F12 / Shift+F12
Breakpoint gutter + debug current line + DAP session MonacoEditorDebugMapper (unit) DAP session
Agent applyEdits + version mismatch 0084 MonacoEditorSessionAndApplyEditsTests, IdeMcpEditorOrchestrator.TryReplaceTextRange WebView apply + stale version
Карта намерений: caret, reveal, CF glyphs EditorNavigationLineMappingTests, EditorControlFlowLanePolicyTests Map click + reveal
Correspondence / anchors / CRS / MCP reveal AnchorDraftPreviewResolverTests, IdeMcpServerDispatchTests CRS → editor
Chat bracket ghost preview AnchorDraftPreviewResolverTests (resolve); reveal — guard only Composer [M:…] ghost
Markdown preview ← EditorText VM MarkdownPreviewRoutingTests MFD preview live
Тема читаема (dark+ / CascadeTheme) MonacoEditorThemeMapper (partial) Visual M11
Память / latency (несколько вкладок) Perf smoke

Чекбоксы ниже — ручной sign-off; CI закрывает регрессию brains + bus contract.

  • Completion / hover / signature — .cs с solution и без
  • Diagnostics squiggles + Problems panel sync
  • Go to definition / references (LSP или Roslyn)
  • Breakpoint gutter + debug current line + DAP session
  • Agent applyEdits + version mismatch handling 0084
  • Карта намерений: caret, reveal, CF glyphs
  • Correspondence / anchors: CRS reverse anchor → editor; reveal_editor_range; Markdown cascade-code-anchor:; Intercom reveal
  • Chat bracket ghost preview
  • Markdown preview ← EditorText VM
  • Тема читаема (dark+ / CascadeTheme)
  • Память / latency приемлемы на целевой машине (несколько вкладок)

5. Последствия

Положительные

  • Один forward stack; снятие класса Avalonia IBackgroundRenderer / EditorIntelligence debt.
  • Нативный UX ближе к VS Code; проще нанимать/переиспользовать паттерны из экосистемы Monaco.
  • Чёткая граница: C# = brains, Monaco = face, bus = contract.
  • LSP и Roslyn сосуществуют через router — без форка логики в JS.

Отрицательные

  • WebView2 process per heavy editor surface; Linux/macOS WebView — отдельный риск (0162).
  • Две темы до M11 (Avalonia chrome + Monaco editor).
  • Миграция bridge v1 → CECB — тактический churn.
  • Monarch ≠ TextMate 1:1 — ручная работа для экзотических грамматик.

Не входит в scope

  • Photino-shell / Electron
  • Forge write path WebView
  • Замена Skia Intercom / cockpit
  • Полный отказ от AvaloniaEdit в тестовых фикстурах до M10

6. Отклонённые альтернативы

Альтернатива Почему нет
Оставить dual-path навсегда Двойная стоимость; Avalonia forward не догонит Monaco по UX
Весь LSP в JS (monaco-languageclient в странице) Дублирует CSharpLspClient в C#; граница доверия хуже
Прямой JSON-RPC LSP из WebView в OmniSharp Обходит agent/MCP/DAL; сложнее auth и lifecycle
Только Roslyn, без LSP Нет solution-wide refs/defs; против LANGUAGE-SERVICES-PLAN.md
monaco-textmate для всех языков Раздувает bundle; Monarch+builtin достаточно для v1
Расширять AvaloniaEdit adorners до parity Уже отвергнуто опытом M0–M3+

7. Ownership (sketch)

Компонент Расположение
ICideEditorCapabilityRouter, IEditorNavigationService, capability handlers Features/Editor/Application/Monaco/
Bus manifest (setId, message types) Features/Editor/Application/Monaco/CideEditorBusManifest.cs + cide-editor/bus-manifest.json
MonacoEditorHostControl Features/Editor/Presentation/
Provider registry + decoration layers Assets/cide-editor/cide-editor-host/ (split from monolithic bridge)
Monarch grammars Assets/cide-editor/lang/*.monarch.js
LSP adapter Services/Lsp/ (existing) — invoked from router

8. Конфиг (целевой)

[editor]
forward_host = "monaco_webview2"   # единственное поддерживаемое значение после M10

[editor.monaco]
# будущее: semantic_tokens = true, code_lens = true, sticky_scroll = "monaco" | "avalonia_banner"

До M10: deprecated avalonia_edit может остаться в parser с warning в log.


История изменений

Дата Изменение
2026-06-21 Proposed: полный перевод Forward на Monaco; CIDE Editor Capability Bus; Monarch/LSP стратегия; фазы M7–M11
2026-06-25 §4: таблица automated-by + Category=MonacoForward test pack (CascadeIDE.Tests/MonacoForward/)
View only · write via MCP/CIDE