Forge
markdowndeeb25a2
1<!-- English translation of adr/0112-command-palette-query-modes-strategy.md. Canonical Russian: ../../adr/0112-command-palette-query-modes-strategy.md -->
2
3# ADR 0112: Command palette query modes (`f:` / `t:` / `m:` / `x:` / `c:`) — mode model, strategies, and workspace search **backends**
4
5**Status:** Accepted · Implemented
6**Date:** 2026-05-11 · updated 2026-05-12
7
8## Related ADRs
9
10| ADR | Role |
11|-----|------|
12| [0013](0013-command-surface-and-discoverability.md) | palette, keyboard-first |
13| [0030](0030-command-ids-hotkeys-and-ui-registry-layers.md) | `IdeCommands`, palette catalog |
14| [0060](0060-keyboard-chord-stack-fms-tactical-strategic.md) | Command Melody `c:` and CascadeChord — orthogonal to palette **line** prefixes |
15| [0070](0070-command-palette-direct-overlay-surface.md) | palette as overlay |
16| [0079](0079-ide-display-system-ids-overlay-pipeline.md) | IDS, overlay hints |
17| [0081](0081-parametric-intent-melodies-editor-line-ranges.md) | parametric tail after alias in `c:` |
18| [0109](0109-declarative-parametric-melody-catalog-toml-and-code-binders.md) | melody catalog |
19| [0105](0105-hybrid-codebase-index-for-csharp-web.md) | Hybrid Codebase Index (core + MCP) for C# stacks with Roslyn truth |
20| [0106](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md) | HCI integration in CascadeIDE |
21
22### Outside ADR
23
24| Document | Role |
25|----------|------|
26| [intent-melody-language-v1.md](../../intent-melody-language-v1.md) | Intent Melody Language v1 |
27
28**Code touchpoints (actual baseline):** `IdeCommandPaletteFilterOrchestrator`, `IntentMelodyAliases.TryGetTail`, `GoToAllQueryParser`, `CommandPaletteChromeProjection`, `IdeCommandPaletteExecutionOrchestrator`.
29
30### Implementation snapshot
31
32| Item | Value |
33|------|-------|
34| — | stages 1–4: mode parse model, chrome, rg/hci/auto backends, TOML |
35| — | per-mode strategies — see `CommandPaletteParsedQueryParser` and orchestrator branches |
36
37## Summary
38
39- Palette (**Ctrl+Q**): query line modes (`t:`/`m:`/`x:`), backend strategies.
40- Workspace search contract and switching in `settings.toml`.
41- Direct overlay surface — [0070](0070-command-palette-direct-overlay-surface.md).
42
43---
44
45## Context
46
47Palette hotkey (default **Ctrl+Q**) opens **one query line** and **one result list**, but input semantics are **not uniform**:
48
49| Prefix | Working name | Query meaning | Where defined today |
50|--------|--------------|---------------|---------------------|
51| *(none)* | command catalog | fuzzy on title/`command_id` | orchestrator default branch |
52| `f:` | go to file | filter solution files | `GoToAllQueryParser` + `RefreshGoToPaletteFilter` |
53| `t:` | go to type | search types in `.cs` | same |
54| `m:` | go to member | heuristic member search in `.cs` | same |
55| `x:` | text | ripgrep over workspace | same |
56| `c:` | Command Melody | alias → `command_id`, parametric `:start:end` | `IntentMelodyAliases.TryGetTail` + `RefreshMelodyPaletteFilter` |
57
58Footer/placeholder hints duplicate this set in text (**`CommandPaletteChromeProjection`**), while **mode branching** sits in **`if` chain at start of `RefreshCommandPaletteFilter`**: first `c:`, then `f|t|m|x`, else catalog.
59
60**Problem:** mode is an **unnamed concept in code**; check order, async go-to cancel (`goToHandle.Cancel()`), row result types, and UI quirks (`IdeCommandPaletteRowViewModel`: melody vs catalog vs go-to) are spread without one “palette mode” contract.
61
62Risks on growth: new prefix or priority change (e.g. future `w:`) touches many places; mode tests are unstructured.
63
64---
65
66## Decision
67
68Introduce two aligned layers:
69
701. **Query line mode** (what user typed) — **`CommandPaletteQueryMode`** / **`CommandPaletteParsedQuery`** (§1–2).
712. **Workspace search backend** for `t:` / `m:` / `x:` submodes — **separate contract** with pluggable implementations and switch config (§7). Not a UI mode: same prefix can use different engines without changing palette syntax.
72
73Mode handling as **strategy per mode** (Strategy pattern / small registry — implementation detail not fixed here). Go-to strategy for `t:`/`m:`/`x:` **delegates** to chosen backend (or fallback chain), not direct `rg` calls from orchestrator.
74
75<a id="adr0112-p1"></a>
76
77### 1. Model
78
79- **`CommandPaletteParsedQuery`** (or equivalent): discriminated union:
80 - **Melody** — normalized tail after `c:` (as `TryGetTail` today);
81 - **GoTo** — `GoToAllQuery` (prefix `f` | `t` | `m` | `x` + term);
82 - **Catalog** — string without reserved mode prefix (trim + fuzzy on catalog).
83
84Document **parse priority** (compatibility with current behavior):
85
861. `c:` — first (so `c:` does not collide with go-to and catalog);
872. then `f:` / `t:` / `m:` / `x:`;
883. else catalog.
89
90<a id="adr0112-p2"></a>
91
92### 2. Strategy per mode
93
94Per mode — **one entry**: filter context (solution, roots, file path, editor text, hotkeys, UI mode family, …) + **action**:
95
96- clear/fill `filteredEntries`;
97- set selected index;
98- call `refreshCommandPaletteSurfaceSnapshot`;
99- for go-to — manage **cancellation** of background search (`CommandPaletteGoToAsyncHandle`), as today.
100
101**Refactoring goal:** `RefreshCommandPaletteFilter` becomes **dispatcher**: `parse → lookup strategy → execute`, without duplicating “who cancels go-to” rules.
102
103<a id="adr0112-p3"></a>
104
105### 3. Link to execution (Enter)
106
107**Execution** of selection is partly unified via `IdeCommandPaletteRowViewModel.RowKind` and `IdeCommandPaletteExecutionOrchestrator` (MCP command vs go-to navigation). Filter mode and RowKind must stay **aligned**; introducing strategies must not change user-facing row contract without separate ADR.
108
109<a id="adr0112-p4"></a>
110
111### 4. Chrome and discoverability
112
113**`CommandPaletteChromeProjection`** texts (footer / placeholder) should **build from canonical mode list** (prefix name + short label) so new mode is added **in one data place**, not by copying `"f: file · …"`.
114
115<a id="adr0112-p5"></a>
116
117### 5. Testability
118
119- Unit tests on **parse** raw string → mode + payload (edge cases: `c:` without tail, `x:` only spaces; no expected `c:` vs catalog conflict in v1).
120- Where possible — tests on **strategy order** (regression: `c:something` does not fall through to catalog as plain text without product decision).
121
122<a id="adr0112-p6"></a>
123
124### 6. Go-to backend: ripgrep vs **HCI** (Hybrid Codebase Index)
125
126Using **`SearchHybridAsync`** / index instead of **`RipgrepWorkspaceSearchService`** for `t:` / `m:` / `x:` is **not automatically** “faster and better” for all modes — depends on **mode goal** and **index freshness**.
127
128| Prefix | Today (baseline) | HCI as candidate | Nuances |
129|--------|------------------|------------------|---------|
130| `f:` | solution tree, in-memory path filter | usually **not needed** | Bottleneck is not disk search; index little help. |
131| `x:` | literal via ripgrep over workspace | **often fits**: FTS on indexed chunks | Plus: no `rg` process, predictable delay with **warm** index. Minus: until reindex — **desync** with disk; FTS semantics (tokens, AND) ≠ “like rg”; literal editor-like search may need **rg fallback**. |
132| `t:` / `m:` | **regex on `.cs`** (`GoToPaletteRipgrepPatternBuilder`) | **debatable without hit model work** | HCI indexes **text fragments**, not necessarily same quality as Roslyn/VS “Go to Type/Member”. Extra/missed lines vs current regex heuristic. Target accuracy for “type/member” closer to **Roslyn** (separate track) than “just FTS”. |
133| (optional) | — | HCI **`semantic`** | Good for **similar meaning**, not strict symbol-name contracts — do not replace `t:`/`m:` without explicit product mode. |
134
135Contract details and switching — §7.
136
137<a id="adr0112-p7"></a>
138
139### 7. First-class workspace search **backend** (`t:` / `m:` / `x:`)
140
141**Boundary:** “backend” applies to **async search in file contents under workspace root** for **`t:`**, **`m:`**, **`x:`**. Mode **`f:`** (path filter on solution tree) **not in this contract** — separate synchronous strategy without “rg-like” engine swap.
142
143**Contract (working name):** abstraction like **`ICommandPaletteGoToSearchBackend`** with unified in/out:
144
145- **In:** normalized `GoToAllQuery`, workspace root, limits (`MaxRipgrepMatches` / equivalent), index scope (aligned with [HCI scope](0106-hybrid-codebase-index-cascadeide-integration-and-semantic-map.md)), `CancellationToken`.
146- **Out:** ordered list of **canonical palette hits** (path, line, column, short label/category — compatible with `IdeCommandPaletteRowViewModel` / existing `CommandPaletteGoTo*NavRowsProjection`).
147
148**Implementations (minimum set):**
149
150| Implementation | Role |
151|----------------|------|
152| **Ripgrep** | Current behavior: `GoToPaletteRipgrepPatternBuilder` + `RipgrepWorkspaceSearchService`. |
153| **HCI-FTS** | Query Hybrid Index (semantic optional), map hit → same DTO. |
154| **Composite / Auto** | Chain: e.g. HCI → on empty, error, or “index not ready” — **fallback** to Ripgrep. Policy explicit, not hidden in orchestrator. |
155
156**Switch configuration** — only in user **`settings.toml`** (`%LocalAppData%\CascadeIDE\`, [0028](0028-user-settings-toml-localappdata-and-secrets.md)): same tree as `CascadeIdeSettings` / `SettingsService.Load`, **not** repo `.cascade/workspace.toml` (`UiWorkspaceToml` — zones, navigation presets, etc.). “How palette searches” is **workstation preference**, not per-repo artifact.
157
158**Schema placement (aligned with existing keys):**
159
160| Option | Verdict |
161|--------|---------|
162| **`[hybrid_index]`** | **Do not** use for `backend`: with value **`rg`** setting is not about index; mixes HCI enablement and independent palette engine choice. `auto` ↔ HCI link — only in facade **code**. |
163| **`[workspace]`** | **Do not** use: in code `WorkspaceSettings` — panels, `Flight`, splitters; unrelated to search backend. |
164| **`[command_palette…]`** | **Yes**: new top-level property on `CascadeIdeSettings`, nested TOML table like `[display.screens.grammar]` — snake_case keys. |
165
166Implementation mapping (guide): `CascadeIdeSettings.CommandPalette.GoToSearch.Backend` → **`[command_palette.go_to_search]`**, field **`backend`**.
167
168Canonical backend values:
169
170| Value | Meaning |
171|-------|---------|
172| **`rg`** | **Ripgrep only** (current baseline; recommended **default** until HCI path stable). |
173| **`hci`** | **Hybrid Codebase Index only** (FTS hit → palette rows). |
174| **`auto`** | HCI first; on index not ready, error, or empty response of relevant kind — **fallback** to `rg`. Fallback policy in Composite facade code, not scattered in UI. |
175
176Example (same user file as `[hybrid_index]`):
177
178```toml
179[command_palette.go_to_search]
180backend = "rg" # "rg" | "hci" | "auto"
181```
182
183- **Per-prefix overrides** (`x:` ≠ `t:`) — optional **second iteration** if v1 can ship without them.
184
185**Support and testing:**
186
187- New backend = **one interface implementation** + DI/factory registration; orchestrator does not branch `if (useHci)`.
188- Orchestrator unit tests on **fake backend**; separate tests per implementation (hit ↔ row mapping).
189
190**UX (optional):** short source label in subtitle (“rg” / “index”) or diagnostics only — product choice; user need not see backend name by default.
191
192---
193
194## Rejected / deferred alternatives
195
196- **Plugin prefixes from TOML in v1** — heavier protocol and validation; defer until second prefix source besides code.
197- **Merge `c:` and go-to into one grammar** — breaks IML and VS-style go-to mental model.
198- **Single regex parser for entire line** — possible implementation detail; architecturally **mode type and strategy** matter.
199
200---
201
202## Consequences
203
204- **Positive:** one explicit entry for mode docs; easier prefix and side effects (go-to cancel); less chrome/code drift; **engine switch** (`rg` ↔ HCI ↔ chain) without orchestrator `if` explosion.
205- **Negative:** refactor scope — orchestrator, DI, settings, tests; after interface extraction user behavior in v1 should stay **parity** with Ripgrep backend by default (change only via setting — intentional).
206
207---
208
209## Implementation status
210
211Implemented in app and tests: `CommandPaletteParsedQuery` / `CommandPaletteParsedQueryParser`, `ICommandPaletteGoToSearchBackend` (+ `rg` / HCI / `auto`), `[command_palette.go_to_search]` in settings, canonical hints `CommandPaletteChromeModeHints`.
212
213Further improvements (plugin prefixes, extended `x:` semantics) — open directions per this ADR, not blockers for current scope.
214
View only · write via MCP/CIDE