Forge
markdowndeeb25a2
1# ADR 0150: Slash line — канонический путь и единый резолв (autocomplete · Enter · execute)
2
3**Статус:** Accepted · Implemented
4**Дата:** 2026-05-26
5
6> **Дополнение (2026-05-28):** источник идентичности пути — **только каталог**; parser shape и `IntercomSlashPathBuilder` удалены — [0153](0153-slash-catalog-only-resolution.md).
7
8## Связанные ADR
9
10| ADR | Роль |
11|-----|------|
12| [0119](0119-chat-slash-commands-intercom-surface.md) | Грамматика slash, autocomplete, local execution |
13| [0136](0136-intercom-feed-gutter-and-slash-namespace.md) | `/intercom <group> <verb>` — пути глубже двух уровней |
14| [0125](0125-slash-workspace-file-commands-and-dynamic-completion.md) | Динамический completion после пути |
15| [0140](0140-tci-slash-status-glyphs-and-args-counter.md) | Счётчик args в TCI |
16
17## Проблема
18
19Один ввод в composer описывался **тремя моделями**:
20
211. **Каталог** — полный путь (`/intercom server start`).
222. **Парсер** — `head` + `action` + `ArgsTail` (вложенный `start` оказывается в хвосте).
233. **Autocomplete** — по сегментам; **Enter** — отдельная эвристика popup / `auto_run`.
24
25Симптомы повторялись: Enter коммитит подсказку вместо send; аргумент **склеивается** с командой без пробела; опциональный `base_url` не отличался от «команда завершена».
26
27## Решение
28
29### 1. Единый резолв строки
30
31`SlashLineResolver` по тексту slash-строки возвращает:
32
33| Поле | Смысл |
34|------|--------|
35| `CanonicalPath` | Путь из `intent-catalog` (`/intercom server start`) |
36| `ArgTail` | Текст **после** пути (URL, заголовок темы, …) |
37| `ArgTailKind` | `none` \| `optional` \| `required` |
38| Флаги UI | exact path, пробел после пути, есть ли текст хвоста |
39
40**Autocomplete**, **Enter (send)**, **Runner (args)** читают только этот резолв, не дублируют эвристики.
41
42### 2. Явный `arg_tail` в каталоге
43
44В `[[command.form.slash]]`:
45
46```toml
47arg_tail = "none" # по умолчанию после полного пути — send на Enter
48arg_tail = "optional" # после commit — пробел; можно дописать args
49arg_tail = "required" # без хвоста — не runnable; dynamic completion
50```
51
52Legacy `requires_arg_tail = true|false` остаётся; при отсутствии `arg_tail` маппится в `required` / `none`. Эвристики (completion, дочерние пути, `open`) — только если `arg_tail` не задан.
53
54### 3. Правила UI (норматив)
55
56| `arg_tail` | Popup сегментов после полного пути | Insert после Tab/Enter на сегменте | Enter → send |
57|------------|-----------------------------------|-------------------------------------|--------------|
58| `none` | скрыть | без лишнего пробела | да |
59| `optional` | скрыть; режим ввода хвоста | **пробел** после пути | да (хвост может быть пустым) |
60| `required` | до хвоста — по completion / сегментам | пробел если есть следующий сегмент каталога | только если `ArgTail` непустой |
61
62Резолв строит канонический путь по токенам каталога; runner берёт `ArgTail` из резолва. *(Историческая v1 оставляла `ChatSlashCommandParser.TryParse` — снято в [0153](0153-slash-catalog-only-resolution.md).)*
63
64## Последствия
65
66- Новые slash с опциональным хвостом: **`arg_tail = "optional"`** в TOML, без кода в `IntercomSlashArgsTail`.
67- Тест-матрица: `SlashLineResolverTests` (путь × ввод × runnable × popup).
68- ~~`IntercomSlashPathBuilder` / `TryResolve(parse)`~~ — удалено ([0153](0153-slash-catalog-only-resolution.md)).
69
70## Non-goals
71
72- Переписать парсер на три уровня `SubAction` для всех namespace.
73- Автогенерация `arg_tail` из JSON schema `IdeCommands` без явной записи в TOML (допустимо позже как hint, не как источник правды).
74
75## Альтернативы (отклонены)
76
77| Вариант | Почему нет |
78|---------|------------|
79| Только точечные фиксы в autocomplete | Регрессии на следующей команде |
80| Всегда trailing space после commit | Ломает `none` и auto_run |
81| Один `requires_arg_tail: bool` | Не различает optional и required |
82
83---
84
85## Статус реализации (2026-05-27)
86
87| Элемент | Артефакт |
88|---------|----------|
89| `SlashLineResolver` | `Features/Chat/SlashLineResolver.cs` |
90| Autocomplete / Enter | `ChatSlashAutocomplete`, `ChatPanelViewModel.IntercomComposerKeys` |
91| Execute args | `ChatSlashCommandRunner` → `ArgTail` из резолва |
92| `arg_tail` в каталоге | `IntentMelody/intent-catalog.toml` — без `requires_arg_tail` |
93| Тесты | `SlashLineResolverTests`, `SlashRouteCatalogIndexTests`, `IntentCatalogArgTailPolicyTests` |
94
95Legacy `requires_arg_tail` в TOML не используется; `SlashRouteCatalogIndex` оставляет эвристики только для оверлеев без `arg_tail`.
96
View only · write via MCP/CIDE