Forge
markdowndeeb25a2
1# ADR 0105: Hybrid Codebase Index (ядро + MCP) for C# stacks with Roslyn Truth
2
3**Статус:** Accepted · Implemented
4**Дата:** 2026-05-06
5
6## Связанные ADR
7
8| ADR | Роль |
9|-----|------|
10| [0039](0039-workspace-navigation-affordances.md) | Навигация по workspace — несколько представлений и «текущий файл + связанные» |
11| [0040](0040-lsp-launch-line-settings-toml-presets-and-environment.md) | LSP (C# / Markdown) — командная строка в `settings.toml`: пресеты, опциональные ключи, переопределение через окружение |
12| [0052](0052-agent-contract-cli-and-snapshot-tests.md) | CLI для контракта агента (паритет с MCP) и снапшот-тесты |
13| [0053](0053-semantic-map-control-flow-pfd.md) | Карта намерений и поток управления на PFD (control flow) |
14| [0056](0056-semantic-map-pipeline-adoption.md) | Semantic Map adoption of Skia composition pipeline |
15| [0067](0067-graph-backed-surfaces-contract.md) | Graph-backed surfaces — общий контракт для семейства графовых экранов |
16| [0069](0069-markdown-preview-tool-surface-and-renderer-decoupling.md) | Markdown Preview — инструмент MFD, renderer-first decoupling и отказ от inline preview в документе |
17| [0079](0079-ide-display-system-ids-overlay-pipeline.md) | IDS vs CDS; AXAML индекс — не IDS |
18| [0095](0095-workspace-solution-ide-health-stratification.md) | Три уровня Health — Workspace, Solution, IDE (таксономия каналов) |
19| [0097](0097-cockpit-compute-units-transport-to-channel-dto.md) | Вычислительные блоки кабины (CCU; аналог LRU *Unit*) — слой между транспортом, смыслом и каналом |
20| [0098](0098-semantic-first-document-as-projection.md) | Семантика первична; документ и репозиторий — проекции (Semantic-First) |
21| [0099](0099-ide-databus-typed-events-and-projections.md) | IDE DataBus — типизированные события и проекции состояния |
22| [0100](0100-project-constitution.md) | Конституция проекта |
23| [0101](0101-licensing-and-commercialization-strategy.md) | Лицензирование и стратегия коммерциализации |
24| [0102](0102-data-acquisition-layer-boundary-and-contract.md) | Data Acquisition Layer — граница внешних интерфейсов и адаптеров |
25| [0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md) | Hybrid Codebase Index — интеграция в CascadeIDE, свежесть и Semantic Map |
26## Резюме
27
28- **Hybrid codebase index:** переносимое ядро + MCP; **Roslyn — истина для C#**.
29- SQLite **FTS5** (keyword) + опциональный **vec** (semantic); fusion α/β.
30- Scope: C#, Razor, AXAML, web-стеки в одном workspace; ADR 0106 — интеграция в CIDE.
31
32
33---
34
35<a id="adr0105-glossary"></a>
36
37## Термины и сокращения
38
39Рабочие определения **в рамках этого ADR**; детали алгоритмов — по документации SQLite / выбранного провайдера эмбеддингов.
40
41| Термин | Смысл здесь |
42| --- | --- |
43| **FTS** (*full-text search*) | Полнотекстовый поиск: индекс и запросы по **токенам/словам** внутри текстов документов (файл или чанк), а не только «точное совпадение поля» или поиск по имени файла. |
44| **FTS5** | Пятый модуль полнотекстового поиска **SQLite**: виртуальные таблицы FTS5, инвертированный индекс «термин → вхождения в документах», запросы с учётом релевантности. В этом ADR — основной **keyword**-backend слоя B. |
45| **Инвертированный индекс** | Структура «слово/термин → список документов (и позиций)», на которой строится быстрый FTS; не путать с **графом символов** Roslyn. |
46| **BM25** (*Best Matching 25*, семейство Okapi BM25) | Класс статистических **функций ранжирования** полнотекстовых попаданий: баланс «термин часто в этом документе» vs «термин редок в корпусе». В SQLite FTS5 релевантность задаётся через вспомогательные функции ранга (в т.ч. **`bm25()`**); в тексте ADR «keyword / BM25» означает **полнотекст с таким ранжированием**, а не отдельный движок вне SQLite. |
47| **Keyword-поиск** | Поиск по **совпадению слов/фраз** (через FTS), без обязательного «понимания смысла» запроса разными формулировками. |
48| **Эмбеддинг (embedding)** | Вектор фиксированной размерности, полученный моделью из текста (фрагмент кода, абзац, запрос). Похожие по смыслу тексты в идеале получают **близкие** векторы в выбранной метрике. |
49| **Semantic / векторный поиск** | Отбор фрагментов по **близости эмбеддингов** запроса и чанков (косинусная близость и т.п.), а не по совпадению ключевых слов. В ADR обозначается также как **vec** (vector channel). |
50| **Vector store** | Хранилище векторов и метаданных (идентификатор чанка, путь, диапазон строк), с операциями ближайших соседей (ANN / полный перебор на малых объёмах). |
51| **sqlite-vec** | Расширение **SQLite** для хранения и запроса векторов; в этом ADR — опциональный локальный vector store **рядом** с FTS, не заменяя keyword-слой. |
52| **Фьюжн (fusion)** | Объединение списков попаданий из **двух каналов** (здесь FTS и vec): нормализация скоров, взвешенная сумма или эквивалент, итоговый top‑N. См. [§ эскиз фьюжна](#adr0105-impl-sketch-fusion). |
53| **Чанк (chunk)** | Непрерывный фрагмент файла, индексируемый как одна единица FTS/vec (строковое окно, логический блок и т.д.); см. [§ чанкинг](#adr0105-impl-sketch-chunking). |
54| **MCP** | *Model Context Protocol* — транспорт и контракт tools для агентов/IDE; отдельный MCP-сервис индекса описан в [§ развёртывание](#adr0105-deployment). |
55| **DAL** | *Data Acquisition Layer* — слой получения данных из workspace и внешнего мира по [0102](0102-data-acquisition-layer-boundary-and-contract.md). |
56| **CCU** | *Cockpit Compute Unit(s)* — упаковка вычислительных результатов в стабильные DTO для каналов по [0097](0097-cockpit-compute-units-transport-to-channel-dto.md). |
57
58---
59
60<a id="adr0105-context"></a>
61
62---
63## Контекст
64
65CascadeIDE — MCP-first IDE: агенту нужно быстро ориентироваться в кодовой базе и собирать контекст в малом окне модели (или при ограниченном бюджете шагов/вызовов).
66
67Для **любых** .NET/C# решений у нас уже есть “источник истины” для точных семантических операций:
68
69- Roslyn (через roslyn-mcp и IDE wiring) для: diagnostics, go-to-definition, find-usages, rename, symbol-level navigation.
70
71Но Roslyn не решает полностью задачу:
72
73- быстрый “обзор по смыслу” и “первую карту” решения без чтения десятков файлов;
74- полнотекст и ориентация по **Markdown**, конфигам, `.csproj` / `.sln` / `.slnx`, YAML/TOML, **веб-слою** (**Razor/Blazor `.razor`**, HTML/CSS), разметке **Avalonia (`.axaml`)** и другим артефактам **без** семантической модели Roslyn для этих форматов;
75- для **обычного** C#‑проекта (включая сам **CascadeIDE**) тот же гибридный слой даёт быстрый keyword/опц. semantic по **репозиторию целиком** — в том числе по **тексту `.cs`** ([слой B](#adr0105-layer-b): только FTS, не символы), пока переименование/impact остаются на Roslyn;
76- устойчивость между сессиями: “карта” должна жить рядом с проектом/профилем IDE и не требовать каждый раз переобучения агента.
77
78Есть внешние решения (например SocratiCode) с hybrid search + graph + impact, но они добавляют инфраструктурную нагрузку (Docker/Qdrant/Ollama), а также риск лицензий (AGPL) для интеграции в продукт.
79
80Дополнительно: CascadeIDE кроссплатформенный (Avalonia). Мы не хотим делать критичный слой навигации завязанным на Windows-only/драйверы/Docker, но на Linux можем разрешать более “тяжёлые” backend-опции.
81
82---
83
84<a id="adr0105-decision-summary"></a>
85
86## Решение в одном предложении
87
88Ввести **двухслойную модель навигации**: **Roslyn — источник истины для C# семантики**, а рядом — **лёгкий гибридный индекс** по **контуру решения**: веб‑артефакты (`.razor`, MD, HTML/CSS), **Avalonia `.axaml`** (и при необходимости эвристика пары с code-behind `.cs`), конфигурация и сопровождение (**в т.ч. опционально полнотекст по `.cs` как тексту**, без подмены symbol-level операций); keyword + опциональная семантика; минимальная ops‑цена и кроссплатформенность.
89
90---
91
92<a id="adr0105-goals"></a>
93
94## Цели
95
961. **Снизить число шагов агента**: 1–2 вызова → достаточно релевантного контекста для решения.
972. Дать “первую карту” без “прочитать 20 файлов”: топ-файлы/узлы/потоки, входные точки — для **Blazor/Web**, для **Avalonia (AXAML + привязки/имена контролов)** и для **обычного C#**, в том числе при **разработке самого CascadeIDE** на том же стеке инструментов.
983. Сохранить **семантическую корректность**: refactor-impact по C# не эвристический, а Roslyn-based.
994. Работать **без обязательного Docker** (особенно на Windows), с предсказуемой локальной установкой/обновлением.
1005. Быть кроссплатформенным (Windows/Linux/macOS), с optional backend-ускорителями на Linux.
101
102---
103
104<a id="adr0105-non-goals"></a>
105
106## Не-цели (на первом этапе)
107
108- Полный “polyglot dependency graph” по 18+ языкам.
109- Замена Roslyn MCP: Roslyn остаётся истиным слоем для C#.
110- Обязательная векторная БД/контейнеры для базовых сценариев.
111- “Один граф, который всегда прав”: граф/impact вне C# допускает эвристику и требует верификации.
112
113---
114
115<a id="adr0105-architecture"></a>
116
117## Архитектура (в терминах слоёв)
118
119<a id="adr0105-layer-a"></a>
120
121### Слой A: Roslyn Truth (C#)
122
123Использовать Roslyn для:
124
125- diagnostics / code actions;
126- find usages / rename;
127- symbol navigation;
128- (по возможности) call graph / entrypoints в пределах C# проекта.
129
130Этот слой — **точный**, но “дорогой” по workflow: агенту всё равно нужно знать, *что искать*.
131
132<a id="adr0105-layer-b"></a>
133
134### Слой B: Hybrid Index (артефакты вокруг C#, веб-слой, Avalonia AXAML, опционально текст `.cs`)
135
136Индекс для файлов и фрагментов **вне** Roslyn-символики или **как текст** (не как граф типов):
137
138- `.razor`, `.razor.cs` (включая связь partial / file pairing);
139- `.md` / `.mdx`;
140- `.html`, `.css`, `.scss` (включая `@import`, классы/селекторы);
141- базовые конфиги (`appsettings*.json`, `.editorconfig`, `*.props`, `*.targets`, `*.csproj`, `*.slnx`, pipeline YAML, `*.yml`, `*.toml` и т.п.);
142- **`.axaml`** (и типичный code-behind `*.axaml.cs`, если есть): разметка и атрибуты — **как текст для FTS** и лёгких эвристик (`x:Name`, `{Binding …}`, `Classes=`, пути `avares:`); **не** подмена XAML-Avalonia-парсера, **не** семантика CDS/IDS (см. [0079 — CDS vs IDS](0079-ide-display-system-ids-overlay-pipeline.md#adr0079-cds-vs-ids));
143- **`*.cs` (опция индекса):** только **полнотекст/keyword** (идентификаторы и строки встречаются как совпадения в тексте файла); **rename/find-usages/impact** по-прежнему только Roslyn. В ответах инструмента явно помечать hits по `.cs` как **text-ranked**, чтобы не смешать с символьной истиной.
144
145Индекс предоставляет:
146
147- **keyword / BM25**: строки конфигов, CSS, маршруты Razor, фрагменты `.cs`/`.axaml`/доков;
148- **опционально semantic**: поиск “по смыслу” (через embeddings), но без обязательного Docker.
149
150Данные индекса:
151
152- хранятся локально (профиль IDE или рядом с проектом);
153- обновляются инкрементально (watcher + hash);
154- имеют явную версионность формата (чтобы migration не ломала UX).
155
156<a id="adr0105-storage"></a>
157
158#### Storage / backend (baseline)
159
160Рекомендуемая конфигурация по умолчанию (без Docker, кроссплатформенно):
161
162- **Keyword/BM25**: SQLite **FTS5** (локальная БД на диске) как быстрый полнотекстовый индекс.
163- **Semantic vectors (optional)**: SQLite + **`sqlite-vec`** как локальный vector store (включается только при включённой семантике).
164
165Движок здесь — **классический SQLite** (например `Microsoft.Data.Sqlite` или другой провайдер к той же библиотеке SQLite), **не** [WitDatabase](https://github.com/dmitrat/WitDatabase) (`*.witdb`): Wit остаётся для данных приложения CascadeIDE; файл индекса — отдельный SQLite на диске.
166
167Важно: hybrid = **FTS (keyword)** + **vec (semantic)** как два независимых подиндекса, объединяемых на уровне сервиса (ранжирование/фьюжн), а не “магия одной БД”.
168
169<a id="adr0105-layer-c"></a>
170
171### Слой C: Composition (агентский workflow, переносимо)
172
173Дефолтный сценарий агента (вне конкретной IDE):
174
1751. Hybrid search (быстро, дешево) → топ-N фрагментов и карта.
1762. Roslyn navigation для точной проверки/рефакторинга в C#.
1773. Точечное чтение файлов/фрагментов только после поиска.
178
179**Встраивание этого сценария в CascadeIDE** (кнопки, каналы, debounce reindex, CCU/DataBus, Semantic Map) — **[ADR 0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md)**.
180
181<a id="adr0105-deployment"></a>
182
183### Развёртывание: библиотека + отдельный MCP
184
185Индекс оформлять как **общую библиотеку** (ядро: индексация, SQLite, форматы запроса/ответа) и **отдельный MCP-сервер** (тонкий слой stdio + регистрация tools), чтобы:
186
187- использовать поиск **вне контура CascadeIDE** (другие IDE/агенты с MCP, CLI, автоматизация);
188- изолировать тяжёлый процесс (watcher, файлы SQLite, опционально embeddings): перезапуск и обновления не смешиваются с Avalonia/UI.
189
190CascadeIDE может подключать **то же ядро in-proc** или поднимать **тот же бинарник MCP** как дочерний процесс — **идентификаторы и контракт tools** сохраняются общими для обоих сценариев (детали размещения по слоям кабины — [0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md)).
191
192---
193
194<a id="adr0105-config-ux"></a>
195
196## Конфигурация и UX-инварианты
197
198- **Off-by-default по инфраструктуре**: если semantic embeddings требуют внешнего провайдера, это должно быть opt-in.
199- **Кроссплатформенность**: одинаковые tool ids/контракты в MCP, разница только в backend-провайдере.
200- **Работа в малом окне**: ответы инструментов должны быть “компактными по умолчанию” (top-N, с указанием пути/диапазона/score), с отдельной командой для расширения.
201
202---
203
204<a id="adr0105-impl-watchouts"></a>
205
206## На что смотреть при внедрении
207
208Операционные моменты, без которых dogfood и продакшен быстро разочаруют:
209
210<a id="adr0105-impl-watchouts-volume"></a>
211
2121. **Объём и шум.** FTS по всем `*.cs` раздувает индекс и может **засорять топ-N** сырьевыми строковыми попаданиями. Нужны явные **умолчания и фильтры** в `settings.toml` (или эквивалент): игноры/`gitignore`-согласование, маски путей, **ранжирование** (например приоритет документов/конфигов перед «сырым» `.cs`, или наоборот — режим «сначала код»), возможность временно исключить `*.cs` из FTS без отключения остального индекса.
213
214<a id="adr0105-impl-watchouts-freshness"></a>
215
2162. **Свежесть (freshness)** при сохранениях из **CascadeIDE**. Дёшевый инкремент и UX без лагов — **[ADR 0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md)**. В MCP/ядре возможен watcher и инкрементальный reindex; продуктовая связка с сессией редактора — в IDE.
217
218<a id="adr0105-impl-watchouts-hit-kind"></a>
219
2203. **Контракт MCP с первого прототипа.** В структуре ответа поиска — **стабильное поле типа попадания** (например `hit_kind`: `text_fts` / `text_vector` / `symbol_followup_roslyn` или эквивалент), чтобы агент и человек не гадали по свободному тексту. Менять семантику поля позже дороже, чем заложить его в v0.
221
222---
223
224<a id="adr0105-alternatives"></a>
225
226## Альтернативы и почему нет (сейчас)
227
228<a id="adr0105-alt-roslyn-grep"></a>
229
230### A) “Только Roslyn + grep”
231
232Плюсы: минимальная инфраструктура, высокая точность для C#.
233Минусы: слишком много шагов и чтения файлов для агентных сценариев; плохо покрывает docs/config/web и **глобальный** “где упомянуто” по репозиторию, если не считать тяжёлый только-Roslyn обход.
234
235<a id="adr0105-alt-socraticode"></a>
236
237### B) Встроить SocratiCode целиком
238
239Плюсы: готовый hybrid+graph+impact слой, быстрое “orientation” по большому репо.
240Минусы:
241- ops: Docker/Qdrant/Ollama в baseline;
242- корректность графа вне C# зависит от эвристик;
243- **лицензия AGPL** — нежелательна для встраивания в продукт (см. [0101](0101-licensing-and-commercialization-strategy.md)).
244
245<a id="adr0105-alt-lsp-all"></a>
246
247### C) LSP для всего (полный polyglot)
248
249Плюсы: потенциальная семантическая точность по языкам.
250Минусы: слишком большая операционная и интеграционная цена; не решает проблему “малое окно/мало вызовов” без отдельного индекса/ранжирования.
251
252---
253
254<a id="adr0105-consequences"></a>
255
256## Последствия
257
258<a id="adr0105-consequences-positive"></a>
259
260### Положительные
261
262- Агент получает быстрый “первый проход” по решению **и** может **dogfood’ить** тот же индекс при разработке **самого CascadeIDE** и других C#‑репо без ограничения сценарием «только Blazor».
263- Roslyn остаётся “истиной” для опасных операций (rename/impact/diagnostics).
264- Docker становится optional: Windows-friendly baseline, Linux может получать расширенные режимы.
265
266<a id="adr0105-consequences-risks"></a>
267
268### Негативные / риски
269
270- Появляется новый слой данных (индекс) → нужны версии, миграции, наблюдаемость.
271- Есть риск ложных связей в `.razor`/CSS/HTML эвристиках → нужен “confidence” и явная маркировка, что это подсказка.
272- Индекс по `.cs`/`.axaml` как текст может **случайно выглядеть** как «семантический find» → см. [§ на что смотреть при внедрении](#adr0105-impl-watchouts) ([`hit_kind`](#adr0105-impl-watchouts-hit-kind), [ранжирование](#adr0105-impl-watchouts-volume)).
273- Нужно удерживать инструменты компактными, иначе hybrid-индекс может “спамить” контекстом и ухудшить UX.
274
275---
276
277<a id="adr0105-rollout-plan"></a>
278
279## План внедрения (переносимое ядро + MCP): статус
280
281| Шаг | Содержание | Контур ADR 0105 (реализовано) |
282| --- | --- | --- |
283| 1 | MCP-контракты (`search`, `status`, `reindex`, `explain-result`, версия/`hit_kind`); ядро в библиотеке | ✅ репозиторий **`hybrid-codebase-index`** |
284| 2 | Keyword index, инкремент, игноры; FTS по `*.cs` опционально; watcher tool | ✅ |
285| 3 | Razor / AXAML: пары `.razor`↔`.razor.cs`, `.axaml`↔`.axaml.cs`; заголовки эвристик `__hci_*` (директивы, ресурсы, привязки, теги) | ✅ (`HybridCodebaseIndex.Core` augment) |
286| 4 | Embeddings opt-in (`settings.toml`), sqlite-vec optional | ✅ |
287| 5 | IDE workflow + свежесть при сохранениях | → **[ADR 0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md)** |
288| 6 | Умолчания области, чанкинг FTS, фьюжн FTS+vec | ✅ `settings.toml` + hybrid search |
289
290---
291
292<a id="adr0105-impl-sketch-scope-chunk-fusion"></a>
293
294## Эскиз: область индекса, чанки, фьюжн (FTS + vec)
295
296Дополнение к [плану внедрения](#adr0105-rollout-plan): разумные умолчания на спайке, без смены верхнеуровневой архитектуры ([слой B](#adr0105-layer-b), [storage](#adr0105-storage)).
297
298<a id="adr0105-impl-sketch-scope"></a>
299
300### Область индекса
301
302- **Первичный якорь:** активный `.sln` / главный `.csproj` профиля CascadeIDE — тот же workspace-контур, что и Roslyn-сессия.
303- **Расширение по умолчанию:** пути под **корнем workspace**, за вычетом согласованного **`.gitignore`** (при необходимости — `.cursorignore` против агентского шума) и **жёсткого denylist**: `bin/`, `obj/`, `node_modules/`, `.git/`, типовые каталоги кэшей тулов.
304- **Монорепо:** одна БД индекса на пару **(workspace_root, solution_path)**; другое решение того же дерева — отдельный контур индекса (переключение профилем). Поле **`extra_include_roots`** в `settings.toml` для соседних каталогов (доки, внешний KB и т.п.) — **opt-in**.
305
306<a id="adr0105-impl-sketch-chunking"></a>
307
308### Чанкинг для FTS
309
310| Тип | Стратегия |
311| --- | --- |
312| Компактные конфиги, небольшие `.md`, `.razor` в пределах лимита | Один FTS-документ на файл; верхний лимит размера документа (например 256–512 KiB) — конфигурируемо. |
313| Длинные `.md`, `.cs`, `.axaml` | Скользящие **окна по строкам** (ориентир: 80–120 строк, overlap 10–15); стабильный `chunk_id`: путь + диапазон (`start_line` / offset). |
314| `.razor` | По возможности **логические границы** (`@code`, крупные разметочные блоки); если не вышло дёшево — те же строковые окна. |
315
316**Свежесть:** при правке пересобирать только затронутые чанки; для маленьких файлов допускается пересборка целого документа. В ответе инструмента всегда указывать **путь и диапазон строк** (или offset), чтобы агент и человек открывали точку без угадываний.
317
318<a id="adr0105-impl-sketch-fusion"></a>
319
320### Фьюжн keyword (FTS) и semantic (vec), v0
321
3221. Независимо получить **топ‑K** из FTS и из vec (внутренний K, ориентир 20–40; наружу после слияния — компактный top‑N).
3232. **Нормализовать** скоры внутри каждого канала (min-max или rank-based, например `1/(rank+R)`).
3243. Объединить множество уникальных чанков: итоговый score **`S = α·S_fts + β·S_vec`**; если чанка нет в канале — вклад этого канала **0**.
3254. **Дефолт при включённом vec:** `α ≈ 0.65`, `β ≈ 0.35`; при выключенном vec — только FTS.
3265. **Короткий запрос (1–2 токена)** или низкий max `S_vec`: усилить вклад FTS или не смешивать vec (keyword-доминирующий режим).
327
328В DTO по возможности сохранять **оба вклада** (`fts_score`, `vec_score` при наличии) вместе с `hit_kind` и итоговым рангом — чтобы объяснимость («почему в топе») не терялась. Пороги и веса выносить в `settings.toml` без ломки формата ответа на следующих итерациях.
329
330
View only · write via MCP/CIDE