Forge
markdowndeeb25a2
1# ADR 0153: Slash — только каталог (удаление parser shape и legacy loader)
2
3**Статус:** Accepted · Implemented
4**Дата:** 2026-05-28
5
6## Резюме
7
8Идентичность слэш-команды (**какой путь** `/map type file`, `/intercom topic create …`) задаётся **только** `IntentMelody/intent-catalog.toml` и build-time trie `SlashRouteCatalogPathsGenerated`. Параллельный разбор `head` / `action` / `sub_action` (`ChatSlashParsePipeline`, `IntercomSlashPathBuilder`, `TryResolve(parse)`) **удалён**. Autocomplete, preview, Enter и runner используют один `SlashLineResolver` (ADR 0150).
9
10## Связанные ADR
11
12| ADR | Роль |
13|-----|------|
14| [0119](0119-chat-slash-commands-intercom-surface.md) | Slash в composer, local execution, `command_id` |
15| [0136](0136-intercom-feed-gutter-and-slash-namespace.md) | Канон `/intercom …`; отказ от top-level `/topic` |
16| [0150](0150-slash-line-canonical-resolution.md) | `SlashLineResolver`, `arg_tail`, единый резолв строки — **дополняется** этим ADR (источник пути) |
17| [0124](0124-slash-parametric-editor-line-commands.md) | Параметрические хвосты (`wire_class`, сегменты) — **без** отдельной стадии парсера пути |
18| [0125](0125-slash-workspace-file-commands-and-dynamic-completion.md) | Динамический completion после канонического пути |
19| [0154](0154-slash-catalog-domain-object-intent.md) | Семантика `domain` · `object` · `intent`, elision, v1 scope; миграция TOML/loader — deferred |
20
21## Проблема
22
23После [0150](0150-slash-line-canonical-resolution.md) в коде оставались **два контура**:
24
25| Контур | Как определял путь |
26|--------|-------------------|
27| Каталог + `SlashLineResolver` | Longest-prefix по `intent-catalog` |
28| Legacy parser + `TryResolve(parse)` | `Head` / `Action` / `SubAction` + `IntercomSlashPathBuilder` |
29
30Они расходились. Пример регрессии: ввод `/map type file` резолвился как путь `/map type`, а `file` уходил в хвост args — команда не переключала уровень карты.
31
32Дополнительный шум: legacy TOML (`[[slash_route]]`, `[[melody_root]]`), `BuildFromLegacyTables`, ~12 стадий `SlashParse/*`, дублирующие тесты на shape парсера.
33
34## Решение
35
36### 1. Единственный источник пути
37
38```text
39intent-catalog.toml
40 → IntentCatalogLoader (только [[command]] / [[command.form.slash]])
41 → IntentSlashCatalog + SlashRouteCatalogIndex (runtime)
42 → ProtocolDocGen → SlashRouteCatalogPathsGenerated.g.cs (build)
43 → SlashLineResolver.TryResolveSlashLine
44```
45
46Новая команда: **строка `path` в TOML** (+ при необходимости `arg_tail`, handlers). Не новая стадия в C#-парсере.
47
48### 2. API резолва (норматив)
49
50| Метод | Назначение |
51|-------|------------|
52| `SlashLineResolver.TryResolveSlashLine` | Канонический путь + сырой `ArgTail` + `ArgTailKind` + флаги UI |
53| `ChatSlashCommandCatalog.TryResolveInput` | Descriptor для execute/preview: **путь из резолва**, хвост нормализован; **не** требует непустой хвост при `required` (ошибку даёт runner / intercom handler) |
54| `ChatSlashCommandCatalog.TryResolveCanonical` | Lookup с политикой `arg_tail` (`required` → хвост обязателен) — runnable, часть autocomplete |
55| `ChatSlashCommandParser` | Только `IsSlashLine` и `ShouldAutoExecuteAfterAutocompleteCommit` — **без** `TryParse` |
56
57### 3. Удалено из продукта
58
59| Артефакт | Причина |
60|----------|---------|
61| `Features/Chat/SlashParse/*` | Дублировал каталог спец-кейсами |
62| `IntercomSlashPathBuilder` | Собирал путь из parser shape |
63| `ChatSlashCommandCatalog.TryResolve(parse)` | Второй вход в каталог |
64| `ChatSlashCommandParseResult`, `ChatSlashCommandShape` | Модель больше не нужна |
65| `IntentCatalogLoader.BuildFromLegacyTables`, `LoadLegacySlashRoutes` | Каталог только command-first |
66| `requires_arg_tail` в loader/codegen | Заменено явным `arg_tail` в TOML |
67
68### 4. Алиасы пути — только в каталоге
69
70Синонимы вроде `/intercom anchor peek` ↔ `/anchor peek` — **отдельные** `[[command.form.slash]]` с тем же `intercom_handler`, не glue в парсере.
71
72Спец-формы без пробела (`/anchor peekabcd1234`) **не** поддерживаются: канон `/anchor peek <id>`.
73
74### 5. Старые top-level слэши (ADR 0136)
75
76`/topic list`, `/overview`, `/attach selection` и т.п. **не** в каталоге → `TryResolveInput` = false, preview/runner: «нет такой команды». Отдельные `RejectReason` с текстом миграции **не** восстанавливаем (были частью `SlashParseLegacyRejectStage`).
77
78## Последствия
79
80- **Плюс:** один контур, предсказуемый longest-prefix, проще тесты (`TryResolveInput` + `SlashLineResolver`).
81- **Плюс:** codegen trie синхронизирован с bundled catalog на каждой сборке.
82- **Минус:** оверлей TOML без `[[command]]` больше не загружается — нужен command-first файл.
83- **Минус:** пользователи старых коротких путей должны перейти на `/intercom …` или получить явный alias в TOML.
84
85## Non-goals
86
87- `System.CommandLine` / полноценный CLI-парсер для всей строки composer.
88- Автогенерация `arg_tail` из JSON-schema `IdeCommands` без записи в TOML.
89- Восстановление parser shape «на всякий случай» для parametric — хвост режет `SlashLineResolver`; валидация сегментов остаётся в `ParametricSegmentListParser` / binders.
90
91## Альтернативы (отклонены)
92
93| Вариант | Почему нет |
94|---------|------------|
95| Оставить `TryResolve(parse)` как fallback | Снова два источника правды и расхождения |
96| Только починить `/map type file` в одной стадии парсера | Следующая команда снова ломается |
97| Combinator-библиотека для всех slash | Сложность без выигрыша при полном каталоге путей |
98
99## Дополнение к ADR 0150
100
101Секции 0150 про «парсер **не меняем** в v1» и «`IntercomSlashPathBuilder` остаётся для `TryResolve(parse)`» **отменены** этим ADR. Актуальны: `SlashLineResolver`, `arg_tail`, правила UI по `ArgTailKind`.
102
103## Статус реализации
104
105| Элемент | Артефакт |
106|---------|----------|
107| Codegen trie | `tools/CascadeIDE.ProtocolDocGen/IntentCatalogSlashPathCollector.cs` → `Services/Generated/SlashRouteCatalogPathsGenerated.g.cs` |
108| Резолв | `SlashLineResolver.cs`, `ChatSlashCommandCatalog.cs` |
109| Runner / preview / send | `ChatSlashCommandRunner.cs`, `SlashCommandPreviewRules.cs`, `ChatPanelViewModel.IntercomSend.cs` |
110| Каталог | `IntentMelody/intent-catalog.toml` |
111| Тесты | `ChatSlashCatalogTestSupport`, `SlashLineResolverTests`, обновлённые `ChatSlash*` / `IntercomAnchor*` |
112
View only · write via MCP/CIDE