| 1 | # ADR 0130: Подсветка диапазона кода для агента без изменения selection |
| 2 | |
| 3 | **Статус:** Accepted · Implemented |
| 4 | **Дата:** 2026-05-19 |
| 5 | |
| 6 | ## Связанные ADR |
| 7 | |
| 8 | | ADR | Роль | |
| 9 | |-----|------| |
| 10 | | [0128](0128-intercom-attachment-anchors-and-code-references.md) | Якоря в Intercom; **reveal из ленты** (`intercom.reveal_attachment`) — тот же presentation mode | |
| 11 | | [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md) | `command_id`; паритет MCP / UI | |
| 12 | | [0013](0013-command-surface-and-discoverability.md) | Discoverability; `highlight_control` как прецедент | |
| 13 | | [0111](0111-editor-linenumber-linerange-value-objects.md) | `LineRange` 1-based в домене и MCP args | |
| 14 | | [0084](0084-agent-edits-editor-source-of-truth-presence-channel.md) | Редактор — источник правды; reveal не подменяет правку | |
| 15 | | [0124](0124-slash-parametric-editor-line-commands.md) | `/editor line select` — **меняет** selection | |
| 16 | | [0120](0120-primary-work-surface-intercom-or-editor.md) | Активный редактор в Forward / Mfd | |
| 17 | | [0131](0131-editor-slash-select-code-by-bracket-reference.md) | `/editor select code [M:…]` — тот же bracket-parse, **select** в буфере, не attach | |
| 18 | |
| 19 | ### Вне ADR (playbook) |
| 20 | |
| 21 | | Документ | Роль | |
| 22 | |----------|------| |
| 23 | | [intercom-ux-reference-slack-mattermost-v1.md](../design/intercom-ux-reference-slack-mattermost-v1.md) | Reveal = рамка, не selection; паритет с `highlight_control` | |
| 24 | | [cascade-ide-ui-layout-v1.md](../ui-ux/cascade-ide-ui-layout-v1.md) | `AgentHighlightOverlay` для контролов | |
| 25 | |
| 26 | ## Резюме |
| 27 | |
| 28 | Зафиксировать **безопасный показ участка кода человеку по запросу агента** (MCP), **без** сообщения в Intercom и **без** изменения `Selection` / каретки в буфере редактора. |
| 29 | |
| 30 | 1. **`command_id`:** `editor.reveal_range` · MCP: `reveal_editor_range` / `ide_reveal_editor_range`. |
| 31 | 2. **Эффект:** при необходимости open файла → scroll into view → **transient** фон строк (gutter-band, `IBackgroundRenderer`) ~3 с; опционально рамка по цвету агента (`#FF6B9D`, как `highlight_control`). |
| 32 | 3. **Не делает:** `SelectInEditor`, `ApplyEdit`, attach в event log. |
| 33 | 4. **Ортогонально:** `go_to_position` (агент готовит правку → select); `intercom.reveal_attachment` (клик человека из ленты по `AttachmentAnchor`). |
| 34 | |
| 35 | **v1 реализация:** line-based `file_path` + `start_line` + `end_line`; общий сервис `EditorAgentRangeReveal` на активном `TextEditor`. |
| 36 | |
| 37 | **Фаза 2 (реализовано):** опциональные `member_key`, `syntax_scope` (JSON), `duration_ms`; re-resolve в `.cs` через `AttachmentAnchorRoslynResolver` (один файл, documentation id или простое имя; scope — `for`/`if`/… + `indexInParent`). |
| 38 | |
| 39 | **Не входит (позже фазы 3+):** overlay-рамка поверх глифов; длительность reveal в TOML (сейчас — `duration_ms` в MCP); паритет hotkey «reveal range» для человека; полный MSBuildWorkspace по solution. |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## Контекст |
| 44 | |
| 45 | Сегодня агент, чтобы «показать» фрагмент, вынужден вызывать **`go_to_position`**, который внутри вызывает **`SelectInEditor`**. Это меняет выделение в буфере: оператор может случайно затереть фрагмент одной клавишей после подсказки агента. |
| 46 | |
| 47 | Для **контролов** уже есть **`highlight_control`**: transient рамка (`UiAgentHighlight` / `AgentHighlightOverlay`), без чата, без изменения состояния контрола. |
| 48 | |
| 49 | Для **кода** [0128](0128-intercom-attachment-anchors-and-code-references.md) описывает тот же UX для **клика по attach** в Intercom, но **не выделяет** отдельный MCP для агента — этот ADR закрывает пробел. |
| 50 | |
| 51 | --- |
| 52 | |
| 53 | ## Решение |
| 54 | |
| 55 | <a id="adr0130-p1"></a> |
| 56 | |
| 57 | ### 1. Термины |
| 58 | |
| 59 | | Термин | Значение | |
| 60 | |--------|----------| |
| 61 | | **Reveal (editor)** | Показать диапазон в редакторе: scroll + transient highlight, **не** selection | |
| 62 | | **Select (editor)** | Изменить `Selection` / каретку — `go_to_position`, `/editor line select` | |
| 63 | | **Attach** | Payload в сообщении Intercom — [0128](0128-intercom-attachment-anchors-and-code-references.md) | |
| 64 | |
| 65 | Reveal и select — **разные команды и разный побочный эффект в буфере**, но **не взаимоисключающие**: обе доступны; для одного жеста (клик по chip) задаётся только **дефолт**, модификатор (Shift) и отдельные MCP переключают режим. |
| 66 | |
| 67 | <a id="adr0130-p2"></a> |
| 68 | |
| 69 | ### 2. `command_id` и MCP |
| 70 | |
| 71 | | Слой | Идентификатор | |
| 72 | |------|----------------| |
| 73 | | Канон | `editor.reveal_range` | |
| 74 | | MCP wire | `reveal_editor_range` | |
| 75 | | Префикс IDE MCP | `ide_reveal_editor_range` | |
| 76 | |
| 77 | **Аргументы v1 (1-based):** |
| 78 | |
| 79 | | Поле | Обязательность | Смысл | |
| 80 | |------|----------------|--------| |
| 81 | | `file_path` | да | Путь к файлу; при необходимости open/activate вкладку | |
| 82 | | `start_line` | да | Первая строка диапазона (inclusive) | |
| 83 | | `end_line` | да | Последняя строка (inclusive); ≥ `start_line` | |
| 84 | |
| 85 | **Возврат:** `OK` или текст ошибки (файл не открыт, нет редактора, невалидный диапазон). |
| 86 | |
| 87 | <a id="adr0130-p3"></a> |
| 88 | |
| 89 | ### 3. Поведение (паритет `highlight_control`) |
| 90 | |
| 91 | | Шаг | Действие | |
| 92 | |-----|----------| |
| 93 | | 1 | Нормализовать `file_path`; если вкладки нет — `open_file` / activate document | |
| 94 | | 2 | Найти `TextEditor` для пути ([`EditorActiveDockResolver`](../Services/EditorActiveDockResolver.cs)) | |
| 95 | | 3 | Прокрутить диапазон в видимую область **без** смены `Caret.Offset` и **без** `Selection` | |
| 96 | | 4 | Установить transient highlight на строки `start_line`…`end_line` (`EditorAgentRangeRevealBackgroundRenderer`) | |
| 97 | | 5 | Снять highlight через ~3 с (как `UiAgentHighlight`) | |
| 98 | |
| 99 | **Цвет v1:** фон `Color.FromArgb(48, 255, 107, 157)`; **одна** рамка pen `#FF6B9D` вокруг **единого** прямоугольника на весь диапазон строк (не построчные «плитки») — визуальная связь с `AgentHighlightOverlay`. |
| 100 | |
| 101 | <a id="adr0130-p4"></a> |
| 102 | |
| 103 | ### 4. Ортогональность |
| 104 | |
| 105 | | Действие | Меняет selection? | Сообщение в Intercom? | |
| 106 | |----------|-------------------|------------------------| |
| 107 | | `editor.reveal_range` | **нет** | нет | |
| 108 | | `go_to_position` | **да** | нет | |
| 109 | | `intercom.reveal_attachment` | нет, если дефолт навигации = reveal; да при select / Shift+клик | контекст из ленты | |
| 110 | | `/attach selection` | нет | **да** (черновик/отправка) | |
| 111 | |
| 112 | <a id="adr0130-p5"></a> |
| 113 | |
| 114 | ### 5. Связь с [0128](0128-intercom-attachment-anchors-and-code-references.md) |
| 115 | |
| 116 | - **Общий presentation mode:** transient range highlight, не selection. |
| 117 | - **Разные входы:** |
| 118 | - **0128** — `AttachmentAnchor` + клик / будущий reveal по anchor id; |
| 119 | - **0130** — прямой MCP от агента по `file_path` + lines (или позже member). |
| 120 | - Реализация **должна переиспользовать** `EditorAgentRangeReveal` (один `IBackgroundRenderer` + таймер), чтобы не плодить два визуальных языка. |
| 121 | |
| 122 | <a id="adr0130-p6"></a> |
| 123 | |
| 124 | ### 6. Фазы |
| 125 | |
| 126 | | Фаза | Содержание | |
| 127 | |------|------------| |
| 128 | | **0** *(текущий PR)* | ADR; `reveal_editor_range`; `EditorAgentRangeReveal` + line range | |
| 129 | | **1** | `intercom.reveal_attachment` вызывает тот же сервис после re-resolve anchor ([0128](0128-intercom-attachment-anchors-and-code-references.md) §8) | |
| 130 | | **2** | Те же поля, что у `AttachmentAnchor` в wire: опциональные **`memberKey`**, **`syntaxScope`** (JSON-объект, не prose `[M:…]`), **`duration_ms`**; re-resolve member/scope у получателя (Roslyn). MCP **не** парсит скобки в тексте сообщения — только структурированные args / `anchor_json` | |
| 131 | | **3** *(done)* | `[intercom.attachments.code].navigate` в TOML; MCP `intercom.reveal_attachment` без `select` → дефолт из настроек; `IntercomAttachmentNavigator` для UI-клика (Shift → select) | |
| 132 | |
| 133 | <a id="adr0130-p6b"></a> |
| 134 | |
| 135 | #### Фаза 3: дефолт жеста, не XOR инструментов |
| 136 | |
| 137 | Фаза 3 **не** выбирает «только reveal или только select» в продукте. Оба остаются: |
| 138 | |
| 139 | | Режим | Когда | Команда / жест | |
| 140 | |-------|--------|----------------| |
| 141 | | Reveal | «Посмотри сюда», буфер не трогать | `editor.reveal_range`, `intercom.reveal_attachment` (дефолт клика), `select: false` в MCP | |
| 142 | | Select | «Работай с этим фрагментом» | `go_to_position`, `/editor line select`, Shift+клик по chip, `select: true` в `intercom.reveal_attachment` | |
| 143 | |
| 144 | **Фаза 3 добавляет только политику по умолчанию** для **одного** UX-жеста — обычный клик по метке attach в ленте ([0128](0128-intercom-attachment-anchors-and-code-references.md) §8): |
| 145 | |
| 146 | - `[intercom.attachments.code]` `navigate = "reveal"` (ожидаемый дефолт) → scroll + transient highlight; |
| 147 | - `navigate = "select"` → `SelectInEditor` после open/re-resolve; |
| 148 | - `reveal_load_solution = "when_needed"` \| `"never"` — грузить .sln перед reveal (см. `IntercomAttachmentsCodeSettings`); |
| 149 | - Shift+клик → select **независимо** от дефолта (как в 0128 §8 шаг 5). |
| 150 | |
| 151 | Агент по-прежнему выбирает **явную** команду: *показать* → `reveal_editor_range`; *править* → `go_to_position`. Дефолт в TOML на клик человека из чата не отменяет MCP. |
| 152 | |
| 153 | --- |
| 154 | |
| 155 | ## Последствия |
| 156 | |
| 157 | **Плюсы:** безопасный «укажи глазами» для агента; симметрия с `highlight_control`; меньше случайных правок после `go_to_position`. |
| 158 | |
| 159 | **Минусы:** два **смысла** навигации по коду — агенту и оператору нужна дисциплина: *показать* → reveal, *править* → select; путать их опасно для буфера, хотя инструменты сосуществуют. |
| 160 | |
| 161 | --- |
| 162 | |
| 163 | ## Отклонённые альтернативы |
| 164 | |
| 165 | | Альтернатива | Почему нет | |
| 166 | |--------------|------------| |
| 167 | | Флаг `highlight_only` у `go_to_position` | Смешивает navigate-for-edit и reveal; ломает контракт существующего тула | |
| 168 | | Только attach в чат | Агент не должен слать сообщение, чтобы подсветить код | |
| 169 | | Всегда selection | Небезопасно для оператора — [0128](0128-intercom-attachment-anchors-and-code-references.md) §8 | |
| 170 | |
| 171 | --- |
| 172 | |
| 173 | ## История |
| 174 | |
| 175 | | Дата | Изменение | |
| 176 | |------|-----------| |
| 177 | | 2026-05-19 | Proposed: отдельный ADR; v1 MCP + `EditorAgentRangeReveal`. | |
| 178 | | 2026-05-19 | Фаза 2: `member_key`, `syntax_scope`, `duration_ms`; Roslyn re-resolve. | |
| 179 | | 2026-05-19 | Уточнение фазы 3: дефолт клика по attach (`reveal` \| `select`), не взаимоисключение инструментов. | |
| 180 | | 2026-05-19 | Accepted: фазы 0–3 в коде (`IntercomSettings`, `IntercomAttachmentNavigator`, MCP default `select`). | |
| 181 | | 2026-05-20 | **Accepted · Implemented**; кластер с [0128](0128-intercom-attachment-anchors-and-code-references.md) / [0131](0131-editor-slash-select-code-by-bracket-reference.md). | |
| 182 | |