ADR 0135: Intercom attach — кэш Roslyn и symbol sidecar (HCI colocation)¶
Статус: Accepted · In progress
Дата: 2026-05-20
Связанные ADR¶
| ADR | Роль |
|---|---|
| 0105 | HCI — FTS/vec по тексту; не symbol truth для C# |
| 0106 | In-proc HCI, watcher, reindex, index_dir |
| 0128 | AttachmentAnchor, member re-resolve @ recipient |
| 0134 | Prepare pipeline; MCP fast path без Roslyn @ send |
Резюме¶
- L1 — in-memory file cache на scope
(workspace, solution): parse/semantic model по.csс инвалидацией поLastWriteTimeUtc. - L2 — symbol line sidecar (SQLite рядом с HCI):
(relativePath, lookupKey) → lineStart/lineEnd, заполняется после HCI reindex и при успешном Roslyn resolve; lookup до полного parse при совпаденииfile_mtime. - Roslyn остаётся истиной при промахе, устаревшем mtime или
syntaxScope— как 0128 §9.1. - Не использовать HCI FTS как единственный источник строк member (хрупко для перегрузок).
Контекст¶
Attach/reveal по [F:… M:…] вызывает ReadAllText + ParseText + мини-compilation на каждый клик/отправку. MCP fast path (0134) откладывает member @ send, но клик и composer всё ещё платят полный Roslyn на холодном файле.
HCI уже индексирует .cs как текст (чанки + LineStart/LineEnd hit). Это не карта «Measure → объявление метода». Пользовательское предложение — «индекс, методы редко меняются» — реализуется отдельной symbol-таблицей, синхронизированной с freshness HCI (reindex / mtime), а не подменой FTS.
Решение¶
L1: IntercomAttachmentRoslynWorkspaceCache¶
| Аспект | Значение |
|---|---|
| Ключ scope | IntercomAttachResolveScopeKey.From(workspaceRoot, solutionPath) |
| Ключ файла | absolute path + LastWriteTimeUtc.Ticks |
| Значение | FileEntry (text, tree, model) — тот же тип, что в per-send session |
| Инвалидация | несовпадение mtime; InvalidateFile; смена scope — новый словарь |
AttachmentAnchorRoslynResolver.TryGetOrCreateEntry сначала session (per-send), затем workspace cache, затем disk read.
L2: IntercomSymbolLineIndex (sidecar SQLite)¶
| Аспект | Значение |
|---|---|
| Путь | {workspaceRoot}/{index_dir}/intercom-symbol-lines.sqlite (тот же index_dir, что 0106) |
| Таблица | symbol_line(scope_key, relative_path, lookup_kind, lookup_key, line_start, line_end, file_mtime_ticks) |
lookup_kind |
docid (DocumentationCommentId), simple (только если имя уникально в файле) |
| Заполнение | IntercomSymbolLineIndexBuilder после HybridIndexOrchestrator reindex; upsert после успешного Roslyn resolve |
| Lookup | перед member walk; при syntaxScope — только L1 (кэш parse) |
Порядок resolve (member, не scope)¶
symbol sidecar (mtime ok)?
→ yes → lines
workspace file cache → parse
Roslyn walk (существующая логика)
→ success → upsert sidecar
MCP / composer¶
| Путь | Поведение |
|---|---|
| MCP @ send | без изменений (0134 fast path) |
| MCP / лента @ click | L2 → L1 → Roslyn |
| Composer @ send | L2 → L1 → Roslyn (быстрее после первого reindex) |
Связь с HCI¶
v0.1.2+ HybridCodebaseIndex.Core: extension point ICodebaseIndexReindexObserver — при reindex HCI вызывает OnFileIndexed с текстом файла; Cascade IDE: IntercomSymbolLineHciReindexObserver пишет symbol sidecar без второго прохода по диску.
Fallback: IntercomSymbolLineIndexCoordinator.ScheduleRebuildAfterHybridIndex — если reindex был без observer (внешний MCP); in-proc IDE observer включён, отдельный rebuild не вызывается.
Не-цели¶
- Замена Roslyn MCP / полного MSBuild workspace compilation.
- Symbol cache для
syntaxScope(for/if index) — только parse cache. - FTS-only resolve member без Roslyn verify.
Последствия¶
- Диск: один SQLite на workspace index dir; рост ~O(символов в .cs).
- Первый reindex после открытия solution — фоновая сборка sidecar; повторные attach/reveal — O(1) lookup.
- Тесты: scope key, sidecar hit/miss по mtime, workspace cache reuse.
Критерии приёмки¶
- Второй подряд reveal того же
[M:Foo]в том же файле без save — заметно быстрее первого (L1/L2). - После save файла — re-resolve не возвращает устаревшие строки (mtime).
- После HCI reindex sidecar содержит строки для тестового метода в известном
.cs. syntaxScopeпо-прежнему работает через Roslyn walk.