ADR 0151: Зерно subgraph control-flow — intent против detailed¶
Статус: Accepted · Implemented
Дата: 2026-05-27
Связанные ADR¶
| ADR | Роль |
|---|---|
| 0053 | Продуктовая цель карты: намерение метода, без «микро-CFG» на PFD |
| 0039 | MCP get_code_navigation_context, режим subgraph |
| 0056 | Общий Skia-пайплайн и представление узлов |
Проблема¶
Один и тот же JSON-контракт subgraph для controlFlow поддерживал пошаговую декомпозицию (условные хвосты, отдельные шаги внутри for и т.д.). Это соответствует «настоящему» локальному CFG, но противоречит продуктовому фильтру из 0053: на карте должно быть видно смысл метода (waypoints), а не копию мелкой структуры исходника.
При этом режим пошаговой разбивки полезен для отладки и для сценариев «покажи всё».
Решение¶
Ввести параметр конфигурации [code_navigation_map].control_flow_grain:
| Значение | Смысл |
|---|---|
intent (по умолчанию) |
Subgraph через CodeNavigationMethodIntentSubgraphBuilder: операторное зерно; циклы for / while / foreach / do — один узел loop_step с заголовком; меньше шума (см. 0053). |
detailed |
Прежнее поведение: CodeNavigationControlFlowSubgraphBuilder (пошаговый CFG по выражениям). |
Нормализация: неизвестные и пустые строки → intent. Ключ в TOML — snake_case, свойство в модели — ControlFlowGrain / NormalizedControlFlowGrain.
Где применяется¶
- Обновление карты в кокпите:
WorkspaceNavigationMapContextJsonBuilderвыбирает билдер по нормализованному зерну. - MCP
get_code_navigation_contextприlevel: controlFlowиспользует те же настройки (McpSettings.CodeNavigationMap), без отдельного аргумента wire (при необходимости аргумент можно добавить позже как override).
Область subgraph (курсор)¶
- Subgraph строится только для области под кареткой: тело метода (включая expression-bodied), local function в top-level, или все top-level statements файла (C# 9+), если курсор в
GlobalStatementSyntax. - Refresh CF читает живую каретку и текст из активного
TextEditor(не только throttled_editorCaretOffset; guard stabilized-input —PathsReferToSameFile). - Нет fallback на «первый метод» файла,
Program.csили якорный узел при загрузке решения без курсора: wireerror: no_control_flow_scope, UI не рисует сцену subgraph. - Refresh CF: позиция только из текущего открытого
.cs, когда путь навигации совпадает с редактором (ResolveControlFlowCursorForRefresh).
Капы subgraph control-flow (UI и MCP по умолчанию): 48 узлов / 96 рёбер (CodeNavigationContextBuilder.DefaultControlFlowSubgraphMax*); при обрезке — truncated_nodes / truncated_edges в JSON. Старый лимит 12 узлов остаётся для режима related / file-subgraph.
Контракт JSON ответа (режим subgraph, поля узлов/рёбер) не меняется — меняется только состав узлов и рёбер.
Последствия¶
- Пользователи с кастомным
settings.tomlбез ключа получаютintent(как дефолт в коде и вdefaults-settings.toml). - Документация MCP: см. MCP-PROTOCOL.md («семантическая навигация»).
Отклонённые альтернативы¶
- Только один режим (только intent): упрощает продукт, но лишает возможности воспроизвести старый граф без форка.
- Отдельный MCP preset вместо TOML: дублирование источников правды; нормы 0039 уже тянут пресеты из
[code_navigation], карта же — секция[code_navigation_map].