Forge
markdowndeeb25a2
1# ADR 0141: Прогрев (warm-up) при открытии solution — отложенная асинхронная оркестрация
2
3**Статус:** Accepted · Implemented (v1: оркестратор, P0–P2, TOML, HIS hint)
4**Дата:** 2026-05-23
5
6## Связанные ADR
7
8| ADR | Роль |
9|-----|------|
10| [0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md) | HCI: `auto_reindex_on_solution_open`, watcher, `HybridIndexStateChanged` |
11| [0135](0135-intercom-attach-symbol-cache-and-hci-sidecar.md) | L1 parse cache, L2 symbol sidecar после HCI reindex |
12| [0128](0128-intercom-attachment-anchors-and-code-references.md) | Attach / bracket re-resolve, chip `resolveOutcome` |
13| [0134](0134-intercom-message-prepare-pipeline-v1.md) | Prepare @ send; fast path без Roslyn |
14| [0138](0138-cockpit-command-line-and-parametric-ranges.md) | TCI preview debounced, Enter не блокируется |
15| [0140](0140-tci-slash-status-glyphs-and-args-counter.md) | Slash preview не блокирует ввод |
16
17Playbook: `docs/design/playbook-solution-warmup-v1.md`.
18
19## Резюме
20
21При **открытии или смене solution** IDE запускает **единый фоновый конвейер прогрева** (warm-up): тяжёлые, но предсказуемые работы выполняются **асинхронно и без блокировки UI**, чтобы последующие действия оператора (bracket `[M:…]`, reveal attach, slash file completion, re-resolve chips в ленте) были **мгновенными или почти мгновенными**.
22
23Принцип: **pay once on solution open, benefit on every interaction** — в духе уже существующего HCI reindex + symbol sidecar, но с **явным каталогом задач**, **приоритетами**, **отменой при смене scope** и **сигналом готовности** (DataBus / HIS), а не разрозненными `Task.Run` в отдельных фичах.
24
25## Контекст
26
27### Что уже есть (фрагментарно)
28
29| Триггер | Что делает | Ограничение |
30|---------|------------|-------------|
31| `SolutionPath` → `ApplyHybridCodebaseIndexOrchestrationForCurrentSolution(pokeWhenAutoReindex: true)` | HCI reindex / watcher ([0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md)) | Не покрывает bracket-autocomplete, L1 до первого parse |
32| После HCI reindex | `IntercomSymbolLineIndexCoordinator.ScheduleRebuild` ([0135](0135-intercom-attach-symbol-cache-and-hci-sidecar.md)) | Стартует **после** HCI; нет связи с «прогреть активный файл» |
33| `ReloadIntercomSessionFromDisk` + `RefreshAttachmentAnchorsForCurrentScope` | Re-resolve chips в ленте при смене workspace/solution | Синхронно на UI после reload; не обновляет event log |
34| `BracketMemberCompletionProvider` | In-memory индекс членов **по запросу** (lazy) | Первый `[` / `M:` на большом `.cs` ранее **фризил UI** — mitigated debounce + без Roslyn на голый `[` (код 2026-05-23), но **нет** proactive warm активного файла |
35| `IntercomAttachmentRoslynWorkspaceCache` (L1) | Parse/model по файлу при resolve | Холодный до первого reveal/send |
36
37Оператор ожидает: открыл solution → подождал N секунд в фоне → дальше `[`, attach, slash **без лагов**. Сейчас часть работы переносится на **первый ввод**, что воспринимается как зависание.
38
39### Проблема
40
41- Нет **единой точки** «что прогреваем при solution open».
42- Нет **отмены** при быстрой смене solution (гонки sidecar vs старый scope).
43- Нет **приоритета** «сначала активный `.cs` в редакторе, потом весь репозиторий».
44- UI не знает **стадию** прогрева (только косвенно через HCI lamp).
45
46## Решение
47
48### 1. `SolutionWarmupOrchestrator` (Application)
49
50Один оркестратор на scope `(workspaceRoot, solutionPath)`:
51
52| Аспект | Значение |
53|--------|----------|
54| Жизненный цикл | Создаётся/сбрасывается при смене `SolutionPath`; `CancellationToken` отменяет предыдущий прогон |
55| Поток | **Никогда** не блокирует UI; тяжёлое — `Task.Run` / очередь с лимитом параллелизма (напр. 2–4 файла) |
56| Публикация | `SolutionWarmupStateChanged` в IDE DataBus ([0099](0099-ide-databus-typed-events-and-projections.md)): `Idle` → `Running` → `Ready` / `Partial` / `Cancelled` |
57
58Точка входа (v1): из `MainWindowViewModel.HandleSolutionPathChanged` **после** постановки HCI в очередь, **без** ожидания завершения HCI.
59
60### 2. Каталог задач прогрева (work items)
61
62Каждая задача — `ISolutionWarmupWorkItem`: `Id`, `Priority`, `RunAsync(scope, ct)`.
63
64| Id | Приоритет | Действие | Потребитель |
65|----|-----------|----------|-------------|
66| `hci.poke` | P0 (уже есть) | Оставить в `HybridIndexOrchestrationPolicy`; оркестратор **не дублирует**, только **подписывается** на `HybridIndexStateChanged` → следующий шаг | Search, orientation |
67| `intercom.symbol_sidecar` | P1 | **Integrated:** `ICodebaseIndexReindexObserver` в Core 0.1.2+ во время HCI reindex (не отдельный обход) | Attach reveal, `[M:…]` L2 |
68| `intercom.feed_anchors` | P1 | `RefreshAttachmentAnchorsForCurrentScope` на UI **после** `symbol_sidecar` **или** сразу с L1-only (два прохода: быстрый + точный) | Chips в ленте |
69| `intercom.bracket.active_file` | **P0** | `BracketMemberCompletionProvider.WarmIndex(absolutePath)` — построить индекс членов для **текущего** `.cs` в редакторе | Composer `[M:…]` |
70| `intercom.bracket.recent_files` | P2 | Опционально: N недавних `.cs` из MRU / открытых вкладок | Bracket file axis |
71| `intercom.roslyn_l1.open_documents` | P2 | Прогреть L1 cache для открытых `.cs` (без полного solution compile) | Reveal, prepare |
72| `slash.workspace_file_index` | P3 | Если есть тяжёлый file index для `/file open` — snapshot в фоне | Slash completion |
73
74**Правило v1:** UI-интеракции **никогда** не ждут `await orchestrator.Ready`; они используют кэш **если есть**, иначе — текущий debounced/lazy путь (как сейчас).
75
76### 3. Порядок и зависимости
77
78```mermaid
79flowchart LR
80 subgraph trigger [Solution open]
81 SP[SolutionPath changed]
82 end
83 subgraph p0 [P0 immediate]
84 AF[bracket.active_file]
85 end
86 subgraph hci [HCI]
87 HCI_Poke[hci.poke]
88 HCI_Done[HybridIndexStateChanged]
89 end
90 subgraph p1 [P1 after HCI]
91 SYM[intercom.symbol_sidecar]
92 ANC[intercom.feed_anchors]
93 end
94 SP --> AF
95 SP --> HCI_Poke
96 HCI_Poke --> HCI_Done
97 HCI_Done --> SYM
98 SYM --> ANC
99```
100
101- **P0** `bracket.active_file` — не ждёт HCI (только файл на диске).
102- **P1** symbol sidecar — **после** HCI (как [0135](0135-intercom-attach-symbol-cache-and-hci-sidecar.md)); feed anchors — после sidecar **или** повторно (cheap merge).
103
104### 4. Контракт «готовности»
105
106| Состояние | Смысл для UI |
107|-----------|----------------|
108| `Running` | Фоновые задачи; bracket/reveal могут использовать partial cache |
109| `Ready` | Все задачи v1 завершены для текущего scope |
110| `Partial` | Отмена/таймаут/ошибка части задач; остальное работает lazy |
111| `Cancelled` | Смена solution до завершения |
112
113HIS / compact status (опционально v1): «Warm-up…» / «Index ready» — не блокирует ввод ([0138](0138-cockpit-command-line-and-parametric-ranges.md), [0140](0140-tci-slash-status-glyphs-and-args-counter.md)).
114
115### 5. Настройки (TOML, фаза B)
116
117```toml
118[solution_warmup]
119enabled = true
120warm_active_file_on_solution_open = true
121warm_feed_anchors_after_symbol_sidecar = true
122max_parallel_file_jobs = 2
123```
124
125До появления секции — поведение как **enabled = true** только для P0+P1 цепочки, остальное off.
126
127## Не-цели
128
129- Полная загрузка MSBuild/Roslyn **solution workspace** при open (слишком тяжело; отдельно `RevealLoadSolution` по [0128](0128-intercom-attachment-anchors-and-code-references.md)).
130- Блокировка Enter / отправки сообщения до `Ready`.
131- Переписывание event log attach при feed refresh (in-memory UI достаточно для v1).
132- Прогрев **всех** `.cs` в монорепо без лимита (только active + bounded queue).
133
134## Альтернативы
135
136| Вариант | Почему не выбран как единственный |
137|---------|-----------------------------------|
138| Только lazy + debounce на вводе | Уже делаем; не убирает «первый раз тормозит» |
139| Только HCI | Не покрывает bracket L1 и feed chips |
140| Синхронный прогрев на UI при open | Регресс UX (зависание главного окна) |
141
142## Последствия
143
144- **Код:** `Features/SolutionWarmup/Application/` (оркестратор, work items), тонкая связка в `MainWindowViewModel` + подписка `ChatPanel` на `feed_anchors` pass.
145- **Тесты:** отмена при смене scope; P0 warm вызывается один раз; mock work item order.
146- **Документация:** `playbook-solution-warmup-v1.md` — чеклист для агента (что уже прогрето, что ещё lazy).
147- **Связь с фиксом 2026-05-23:** debounce bracket + кэш `BracketMemberCompletionProvider` — **первая линия обороны**; ADR 0141 — **стратегическая** линия (proactive).
148
149## Фазы внедрения
150
151| Фаза | Содержание | Критерий |
152|------|------------|----------|
153| **A** | `SolutionWarmupOrchestrator` + P0 `bracket.active_file` + cancel on scope change | После open solution ввод `[M:` в активном `.cs` без заметного лага |
154| **B** | Цепочка HCI → symbol sidecar → `feed_anchors` + `SolutionWarmupStateChanged` | Chips `member_not_found` → `resolved` без клика после warm |
155| **C** | TOML, P2 open documents / recent files, HIS hint | Настраиваемо; статус в MFD |
156
157## Критерии приёмки (фаза A+B)
158
1591. Смена solution отменяет предыдущий прогрев (нет записи в sidecar со старым `scope_key`).
1602. Активный `.cs` в редакторе: bracket autocomplete по `M:` после open **не парсит с нуля на UI** (индекс уже в cache).
1613. Лента Intercom: после завершения P1 якоря с `member_not_found` обновляют chip без reveal (если member есть в workspace).
1624. Ввод в composer **не блокируется** во время прогрева (регресс-тест на `[` и `/`).
163
164## Открытые вопросы
165
166- Нужен ли **второй** проход `feed_anchors` только по видимым сообщениям ленты (viewport-limited) vs вся история сессии?
167- Делить ли очередь прогрева с HCI file I/O (общий `SemaphoreSlim`) для SSD/CPU?
168
View only · write via MCP/CIDE