From 847f4c216ba12c0f1bcf134dcbf5b3f09c2fb0a2 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Mon, 1 Jun 2026 01:26:05 +0900 Subject: [PATCH 1/2] Document hotspot grouped count semantics (#1596) --- USER_GUIDE.md | 4 ++-- changelog.d/unreleased/1596.docs.md | 18 ++++++++++++++++++ src/CodeIndex/Cli/CliFlagSchema.cs | 2 +- src/CodeIndex/Cli/ConsoleUi.cs | 2 +- tests/CodeIndex.Tests/ConsoleUiTests.cs | 2 +- .../CodeIndex.Tests/QueryCommandRunnerTests.cs | 3 ++- 6 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 changelog.d/unreleased/1596.docs.md diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 2db44c2e66..261bc64ff5 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -1115,7 +1115,7 @@ same source location. | `--body` | `definition`, `references`, `callers`, `callees`, `impact`, `inspect` | Include reconstructed body content or capped graph-location excerpts | | `--count` | `search`, `definition`, `references`, `callers`, `callees`, `symbols`, `files`, `find`, `impact`, `unused`, `hotspots` | Return only counts. `search` / `definition` / `references` / `callers` / `callees` / `symbols` / `files` / `find` / `unused` ignore `--limit` and return authoritative totals; `impact` and `hotspots` still report the visible page count and may truncate with `--limit` (with `--json`: a single count object; commands that expose file counts add `files`) | | `--group-by ` | `hotspots` | Choose the hotspot grouping unit. The default is `symbol` for non-SQL scopes and `statement` for `--lang sql`, preserving SQL's statement-oriented grouping; JSON includes `grouped_by` so mixed-language callers can verify the active unit. `file` rolls symbol hotspot volume up to target files. | -| `--group-by-name` | `hotspots` | Collapse rows that share the same `(name, kind)` across files into one representative result while preserving `definition_sites` / `paths` metadata in JSON. Hotspot ordering uses a weighted invocation score (`call` / `instantiate` = 1.0, `subscribe` = 0.3) while still showing the raw reference count; metadata-only edges such as `attribute`, `annotation`, and `type_reference` remain excluded from default hotspots. | +| `--group-by-name` | `hotspots` | Collapse rows that share the same `(name, kind)` across files into one representative result while preserving `definition_sites` / `paths` metadata in JSON. The top-level JSON `count` is the number of returned name/kind groups after `--limit`; use `definition_site_total` for the grouped definition-site total, each hotspot's `reference_count` for raw incoming references, and the count-only JSON `files` field for distinct files. Hotspot ordering uses a weighted invocation score (`call` / `instantiate` = 1.0, `subscribe` = 0.3) while still showing the raw reference count; metadata-only edges such as `attribute`, `annotation`, and `type_reference` remain excluded from default hotspots. | | `--with-paths` | `impact` | Emit a `paths` array on each caller listing the shortest call chains `[resolvedRoot, intermediate..., callerName]`. Same-depth diamond convergence (e.g. `A → B → foo` and `A → C → foo`) surfaces both routes that the default dedup collapses. Per-row cap (10) keeps JSON payloads bounded; `paths_truncated` signals overflow. Off by default; default behavior is unchanged. | | `--start ` | `excerpt` | Start line for excerpt reconstruction (max: 10000000) | | `--end ` | `excerpt` | End line for excerpt reconstruction (defaults to `--start`; max: 10000000) | @@ -3183,7 +3183,7 @@ raw match density を正確に測る、といった理由で全 raw chunk hit | `--body` | `definition`, `references`, `callers`, `callees`, `impact`, `inspect` | 再構成した本文、または上限付きの graph 位置抜粋を含める | | `--count` | `search`, `definition`, `references`, `callers`, `callees`, `symbols`, `files`, `find`, `impact`, `unused`, `hotspots` | 件数だけを返す。`search` / `definition` / `references` / `callers` / `callees` / `symbols` / `files` / `find` / `unused` は `--limit` を無視した総件数を返し、`impact` と `hotspots` は visible page count のままで `--limit` によって切り詰められることがある(`--json` 併用時は単一の count オブジェクト。files 件数を出すコマンドは `files` も返す) | | `--group-by ` | `hotspots` | hotspot の集計単位を選ぶ。既定は非 SQL scope では `symbol`、`--lang sql` では既存の statement-oriented grouping を保つため `statement`。JSON には `grouped_by` が入り、mixed-language 呼び出しでも現在の単位を確認できる。`file` は symbol hotspot の参照量を対象ファイル単位にまとめる。 | -| `--group-by-name` | `hotspots` | ファイルをまたいで同じ `(name, kind)` を共有する行を代表1件に集約し、JSON では `definition_sites` / `paths` metadata を保持したまま返す。hotspot の順位付けは重み付き invocation score(`call` / `instantiate` = 1.0、`subscribe` = 0.3)を使い、生の reference count も引き続き表示する。`attribute` / `annotation` / `type_reference` のような metadata-only edge は既定の hotspots から除外されたまま。 | +| `--group-by-name` | `hotspots` | ファイルをまたいで同じ `(name, kind)` を共有する行を代表1件に集約し、JSON では `definition_sites` / `paths` metadata を保持したまま返す。top-level JSON の `count` は `--limit` 適用後に返された name/kind group 数であり、grouped definition-site 合計は `definition_site_total`、生の incoming reference 数は各 hotspot の `reference_count`、distinct file 数は count-only JSON の `files` field を参照する。hotspot の順位付けは重み付き invocation score(`call` / `instantiate` = 1.0、`subscribe` = 0.3)を使い、生の reference count も引き続き表示する。`attribute` / `annotation` / `type_reference` のような metadata-only edge は既定の hotspots から除外されたまま。 | | `--with-paths` | `impact` | 各 caller に `paths` 配列を付け、`[resolvedRoot, 中間..., callerName]` の順で最短呼び出し経路を列挙する。同 depth で複数経路が収束するダイヤモンド(例: `A → B → foo` と `A → C → foo`)でも、既定 dedup で潰れる経路をすべて表示する。1 行あたりの保持上限は 10 経路で、超過時は `paths_truncated` を `true` にする。既定では出力しないため、フラグ未指定時の挙動は変更しない。 | | `--start ` | `excerpt` | 抜粋再構成の開始行(最大: 10000000) | | `--end ` | `excerpt` | 抜粋再構成の終了行(省略時は `--start` と同じ、最大: 10000000) | diff --git a/changelog.d/unreleased/1596.docs.md b/changelog.d/unreleased/1596.docs.md new file mode 100644 index 0000000000..b5e9feae34 --- /dev/null +++ b/changelog.d/unreleased/1596.docs.md @@ -0,0 +1,18 @@ +--- +category: docs +issues: + - 1596 +affected: + - USER_GUIDE.md + - src/CodeIndex/Cli/ConsoleUi.cs + - src/CodeIndex/Cli/CliFlagSchema.cs + - tests/CodeIndex.Tests/QueryCommandRunnerTests.cs +--- + +## English + +- **Documented `hotspots --group-by-name` count semantics (#1596)** — the help text and user guide now state that grouped JSON `count` is the returned name/kind group count, with separate fields for definition sites, references, and files. + +## 日本語 + +- **`hotspots --group-by-name` の count semantics を明文化しました (#1596)** — help text と user guide で、grouped JSON の `count` は返された name/kind group 数であり、definition site、reference、file は別 field で確認することを明記しました。 diff --git a/src/CodeIndex/Cli/CliFlagSchema.cs b/src/CodeIndex/Cli/CliFlagSchema.cs index 25a301eb49..aa59a2d9b0 100644 --- a/src/CodeIndex/Cli/CliFlagSchema.cs +++ b/src/CodeIndex/Cli/CliFlagSchema.cs @@ -265,7 +265,7 @@ private static IReadOnlyList BuildAll() new() { Name = "--with-paths", Description = "Impact: include shortest call chains per caller", Commands = Set("impact") }, new() { Name = "--reverse", Description = "Reverse direction (show dependents)", Commands = Set("deps") }, new() { Name = "--group-by", ValuePlaceholder = "", Description = "Hotspots: choose grouping unit", Commands = Set("hotspots") }, - new() { Name = "--group-by-name", Description = "Hotspots: collapse same-name rows across files", Commands = Set("hotspots") }, + new() { Name = "--group-by-name", Description = "Hotspots: collapse same-name rows; count is name/kind groups", Commands = Set("hotspots") }, new() { Name = "--check", Description = "Verify status freshness/readiness", Commands = Set("status") }, new() { Name = "--config", Description = "Print effective configuration with source attribution", Commands = Set("status") }, new() { Name = "--stale-after", ValuePlaceholder = "", Description = "Status: freshness age threshold (e.g. 30m, 2h, 7d)", Commands = Set("status") }, diff --git a/src/CodeIndex/Cli/ConsoleUi.cs b/src/CodeIndex/Cli/ConsoleUi.cs index 9ddb85212a..8cbee795e7 100644 --- a/src/CodeIndex/Cli/ConsoleUi.cs +++ b/src/CodeIndex/Cli/ConsoleUi.cs @@ -942,7 +942,7 @@ private static void PrintFlagReference(Action WriteHelpLine) WriteHelpLine(" --max-hops Max BFS hops for impact analysis, inclusive (default: 5; --max-hops 2 returns callers at hop 1 and 2; --max-hops 0 resolves the symbol without traversing callers)"); Console.WriteLine(" --depth Deprecated alias for --max-hops"); Console.WriteLine(" --reverse Reverse direction for deps (show dependents)"); - Console.WriteLine(" --group-by-name hotspots: collapse rows sharing (name, kind) across files into one line"); + WriteHelpLine(" --group-by-name hotspots: collapse rows sharing (name, kind) across files; JSON count is the number of name/kind groups, not reference, file, or definition-site count"); WriteHelpLine(" --with-paths impact: also emit `paths` per caller — the shortest call chains [root, ..., caller] (diamond graphs surface every converging route, capped per row)"); WriteHelpLine(" unused reflection note C# nameof/typeof and direct reflection member-name literals such as GetMethod(\"Foo\") are indexed; dynamically constructed reflection names may need manual review"); WriteHelpLine(" Note: if a query itself starts with '-', pass it with --query or -- ; for option values that start with '--', use --opt=."); diff --git a/tests/CodeIndex.Tests/ConsoleUiTests.cs b/tests/CodeIndex.Tests/ConsoleUiTests.cs index 38daccbd27..8987fd9779 100644 --- a/tests/CodeIndex.Tests/ConsoleUiTests.cs +++ b/tests/CodeIndex.Tests/ConsoleUiTests.cs @@ -730,7 +730,7 @@ public void PrintCompletions_PowerShellIncludesSharedFlagValues() [Theory] [InlineData("bash", "if [ \"$cmd\" = \"hotspots\" ]", "--group-by-name", "--exact-name")] - [InlineData("zsh", "elif [[ $subcmd == hotspots ]]; then", "--group-by-name[Hotspots: collapse same-name rows across files]", "--exact-name[Exact symbol-name equality]")] + [InlineData("zsh", "elif [[ $subcmd == hotspots ]]; then", "--group-by-name[Hotspots: collapse same-name rows; count is name/kind groups]", "--exact-name[Exact symbol-name equality]")] public void PrintCompletions_BashAndZshScopeGroupByNameToHotspots(string shell, string hotspotsBranchMarker, string groupedFlagToken, string genericExactNameToken) { var output = ConsoleUi.GetCompletionScript(shell); diff --git a/tests/CodeIndex.Tests/QueryCommandRunnerTests.cs b/tests/CodeIndex.Tests/QueryCommandRunnerTests.cs index 2df6887174..5fa8d665c0 100644 --- a/tests/CodeIndex.Tests/QueryCommandRunnerTests.cs +++ b/tests/CodeIndex.Tests/QueryCommandRunnerTests.cs @@ -27367,7 +27367,7 @@ public class @class } [Fact] - public void RunHotspots_GroupByNameJson_CollapsesDuplicateDefinitionSites() + public void RunHotspots_GroupByNameJson_CountIsNameKindGroupCount() { var projectRoot = TestProjectHelper.CreateTempProject("cdidx_hotspots_group_json"); try @@ -27414,6 +27414,7 @@ public void Use() Assert.Equal("name_kind", json.GetProperty("grouped_by").GetString()); Assert.Equal("SharedHelper", hotspot.GetProperty("name").GetString()); Assert.Equal("function", hotspot.GetProperty("kind").GetString()); + Assert.Equal(2, hotspot.GetProperty("reference_count").GetInt32()); Assert.Equal(2, hotspot.GetProperty("definition_sites").GetInt32()); Assert.Equal(2, hotspot.GetProperty("paths").GetArrayLength()); } From dbc2fc756df7e23d9044c4df6d8a16c109e31f68 Mon Sep 17 00:00:00 2001 From: Widthdom Date: Mon, 1 Jun 2026 03:06:22 +0900 Subject: [PATCH 2/2] Fix group-by-name usage test expectation --- tests/CodeIndex.Tests/ConsoleUiTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/CodeIndex.Tests/ConsoleUiTests.cs b/tests/CodeIndex.Tests/ConsoleUiTests.cs index 441cf82998..df15f8fef5 100644 --- a/tests/CodeIndex.Tests/ConsoleUiTests.cs +++ b/tests/CodeIndex.Tests/ConsoleUiTests.cs @@ -158,7 +158,7 @@ public void PrintUsage_WithoutBanner_HidesAsciiArtAndEasterEggFlags() Assert.Contains("--json Output as JSON (search streams ndjson by default; use search --json=array for one array)", output); Assert.Contains("--lang Filter by language (aliases: bat, cmd, cshtml, razor, ts, tsx, cts, mts)", output); Assert.Contains("--bytes Show raw byte counts in human output for files/map instead of binary units; JSON always keeps raw integer bytes", output); - Assert.Contains("--group-by-name hotspots: collapse rows sharing (name, kind) across files into one line", output); + Assert.Contains("--group-by-name hotspots: collapse rows sharing (name, kind) across files; JSON count is the number of name/kind groups, not reference, file, or definition-site count", output); Assert.Contains("cdidx search \"Run();\" --exact-substring Case-sensitive exact substring search", output); Assert.Contains("cdidx search --query --path --path README.md Search for a literal option token", output); Assert.Contains("cdidx hotspots --group-by-name --exclude-tests", output);