Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1758,6 +1758,8 @@ Runtime diagnostic subcontracts:

`references` already prefixes each human-readable row with `reference_kind`, and `callers` does the same for its grouped caller rows. When one grouped container mixes kinds (for example `call` and `subscribe` on the same event member), the human-readable label joins the distinct kinds with `+` (for example `call+subscribe`) instead of collapsing to a single preferred label, and the reference-kind column widens dynamically to fit the longest label in the batch so mixed rows do not overrun the neighbouring column. JSON output for `callers` and `callees` keeps the scalar `reference_kind` for back-compat (it reports the preferred summary kind `instantiate` > `subscribe` > `MIN(call)`) and adds a sorted `reference_kinds` array plus a `has_mixed_reference_kinds` bool so consumers can detect mixed containers without trusting a single collapsed label. This lets terminal users distinguish `call` / `instantiate` / `subscribe` / mixed without re-running the command with `--json` and lets AI clients answer mixed-kind questions without chasing a second `--exact` query.

For `references`, `callers`, and `callees`, an explicit `--snippet-lines` is valid only with `--body` and text or JSON result output. Location-only formats and `--count` reject the option before opening the database so a requested snippet length is never silently ignored or recorded in replay/query context without being applied. Explicitness comes from the argument parser, so option-like literals passed through `--query` or after `--` remain queries. Bounded JSON projection also removes the snippet-only control from its internal count replay, preserving clean stderr, total counts, and cursors for visible body excerpts.

`ReferenceResult` includes `is_self_reference` and `is_mutual_recursion`; `CallerResult` includes `has_self_reference` and `has_mutual_recursion`. These fields identify self-recursive edges and direct two-symbol cycles without removing valid recursive calls from default graph results. Reader APIs that need a non-recursive view can opt into self-reference exclusion.

MCP tool calls return structured JSON in `structuredContent` plus a short summary in `content`, so clients can consume typed data directly. Text content blocks include `mimeType`: `application/json` when a structured payload is present and `text/plain` otherwise. Tool input schemas also carry common JSON Schema constraints (`minimum` / `maximum` for limits and line counts, `maxLength` for free text, `pattern` for workspace-relative path filters, and `enum` for common kind values) so MCP-aware clients can reject invalid requests before dispatch.
Expand Down Expand Up @@ -5130,6 +5132,8 @@ runtime diagnostic subcontract:

`references` は以前から人間向け出力の各行先頭に `reference_kind` を表示しており、`callers` も grouped caller 行に対して同じタグを出す。1 つの grouped container で kind が混在する場合(例: 同じ event メンバに対する `call` と `subscribe`)は、単一 preferred label へ潰さずに `call+subscribe` のように distinct kind を `+` で連結して表示する。reference-kind 列の幅はバッチ内で最も長いラベルに合わせて動的に広がるため、mixed 行が隣接列を押し出さない。`callers` / `callees` の JSON 出力では、後方互換のため scalar な `reference_kind`(preferred 順 `instantiate` > `subscribe` > `MIN(call)` の要約 kind)を残しつつ、ソート済みの `reference_kinds` 配列と `has_mixed_reference_kinds` bool も追加した。これにより consumer は単一 summary label に騙されずに mixed container を検出できる。端末上でも `call` / `instantiate` / `subscribe` / mixed を `--json` なしで見分けられ、AI クライアントも `--exact` を改めて投げ直さずに mixed-kind の問いに答えられる。

`references`、`callers`、`callees` で明示した `--snippet-lines` は、`--body` と text または JSON の結果出力を併用する場合だけ有効です。location-only format と `--count` は database を開く前にこの option を拒否するため、要求した snippet 長が黙って無視されたり、適用されないまま replay / query context に記録されたりすることはありません。明示指定かどうかは引数 parser の状態で判定するため、`--query` の値または `--` 以降に渡した option 風の literal は query のままです。bounded JSON projection の内部 count replay からは snippet 専用 control も除去し、表示可能な body excerpt で stderr、total count、cursor を正しく維持します。

`ReferenceResult` は `is_self_reference` と `is_mutual_recursion` を含み、`CallerResult` は `has_self_reference` と `has_mutual_recursion` を含む。これらのフィールドは、正当な再帰呼び出しを既定の graph 結果から削除せずに、自己再帰エッジと直接の2シンボル循環を識別する。非再帰 view が必要な reader API は自己参照除外を opt-in で使える。

MCPツール呼び出しは `structuredContent` に構造化JSON、`content` に短い要約を返すため、クライアントは型付きデータを直接利用できます。
Expand Down
2 changes: 2 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
- `IndexCommandRunner*Tests.cs`, `QueryCommandRunner*Tests.cs`, `ProgramCliTests.cs`, `InstallScriptTests.cs`
YAML `outline` integration coverage must assert nested sequence paths, mapping-parent depth, unchanged source ranges and symbol counts, deterministic sorting, and cursor replay across a page boundary.
CLI parsing, command execution, and installer behavior. Index command coverage is split by run mode or feature area, and query command coverage is split by command family with partial test classes so shared console and fixture helpers stay centralized. Keep repeated query-result fixtures, such as overlapping chunk content used by multiple search deduplication tests, in narrow class-level helpers instead of duplicating local builders. `ProgramCliTests.cs` covers top-level entrypoint behavior that must be exercised through a subprocess, while `InstallScriptTests.cs` runs focused bash snippets against `install.sh` in library mode to lock in release-installer regressions without performing real network installs. Installer bundle-generation tests must also verify that `install.sh` is marked generated while every canonical `install_modules/` source remains unmarked.
Graph snippet-length validation uses one command/format matrix across `references`, `callers`, and `callees`. Keep explicit `--snippet-lines` fail-fast without `--body`, reject location-only and count output even when `--body` is present, preserve the zero/range errors, and retain successful text/JSON body excerpts through the accepted maximum. Also cover option-like literals supplied through `--query` and `--`, plus two-page bounded body projection with clean stderr, correct total counts, and cursor continuation. Top-level compact aliases must validate the original arguments before database access, and bounded count replay must preserve snippet-like query literals.
C# `unused` partial-family coverage must reuse one multi-file fixture across regular JSON, compact, `--by-bucket`, and `--actionable`; include top-level and nested partial types, genuinely unused private members, an unrelated same-named family, a containing-type generic-arity collision, and a family-external occurrence in a matched peer file so semantic membership cannot regress into name-only, flattened-qualified-name, or whole-file evidence sharing.
`CliFlagSchemaTests.cs` and `ConsoleUiTests.cs` keep command help, runtime value validation, and each generated shell's option/value catalog aligned with `CliFlagSchema`, and check every Fish command scope against the shared per-command completion projection. When an accepted value or alias changes, assert its registry normalization plus command usage, runtime validation, and Bash / Zsh / Fish / PowerShell completion visibility; add the value to the schema instead of weakening this parity contract.
`ProgramRunnerTests.cs` enumerates `CliCommandCatalog.CommandSubcommands` and requires every valid nested verb to resolve to verb-specific usage with an example. It also pins destructive index confirmation and aggregate dependency-filter help, plus the read-only GitHub duplicate-preflight boundary for suggestion exports. Installer cancellation coverage must wait until the PID file contains a complete positive integer before cancelling, using a bounded dedicated waiter instead of a fixed-delay timer so parallel load cannot expose a redirection-created empty file.
Expand Down Expand Up @@ -1116,6 +1117,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
- `IndexCommandRunner*Tests.cs`、`QueryCommandRunner*Tests.cs`、`ProgramCliTests.cs`、`InstallScriptTests.cs`
YAML `outline` の integration coverage では、入れ子 sequence path、mapping parent の depth、不変な source range と symbol count、決定的な sort、page 境界を越える cursor replay を検証してください。
CLI の引数解析、コマンド実行、installer 挙動のテスト。Index command coverage は run mode または機能領域ごとの partial suite に分割し、Query command coverage は command family ごとの partial test class に分割して、共有 console / fixture helper は一箇所に保ちます。`ProgramCliTests.cs` はグローバル引数の解釈や完全な CLI 起動フローのように subprocess 経由で確認すべき Program エントリポイント挙動を扱い、`InstallScriptTests.cs` は `install.sh` を library mode で source した bash snippet を実行して、実ネットワーク install を行わずに release installer の回帰を固定する。installer bundle 生成テストでは、`install.sh` が generated と判定される一方、canonical な `install_modules/` source はすべて unmarked のままであることも検証してください。
graph の snippet 長 validation は、`references`、`callers`、`callees` を横断する1つの command / format matrix で検証します。明示した `--snippet-lines` は `--body` なしで fail-fast し、`--body` があっても location-only / count 出力では拒否すること、zero / range error を維持すること、受理上限まで text / JSON の body excerpt が成功することを固定してください。さらに、`--query` と `--` で渡した option 風 literal、および stderr が空で total count と cursor continuation が正しい2ページの bounded body projection も検証してください。top-level compact alias は database access より前に原引数を検証し、bounded count replay は snippet 風の query literal を維持しなければなりません。
C# `unused` の partial-family coverage では、通常の JSON、compact、`--by-bucket`、`--actionable` で1つの multi-file fixture を共有してください。top-level / nested partial type、本当に未使用の private member、無関係な同名 family、containing type の generic-arity collision、matched peer file 内の family 外 occurrence を含め、semantic membership が name-only、平坦化された qualified name、または file 全体の evidence 共有へ戻らないことを固定します。
`CliFlagSchemaTests.cs` と `ConsoleUiTests.cs` は command help、runtime value validation、各生成 shell の option / value 全カタログを `CliFlagSchema` と同期させ、Fish の全 command scope を共有の command 別 completion 射影と照合します。受理値または alias を変更するときは、registry normalization に加えて command usage、runtime validation、Bash / Zsh / Fish / PowerShell completion への露出を検証し、この parity 契約を弱めず schema に値を追加してください。
`ProgramRunnerTests.cs` は `CliCommandCatalog.CommandSubcommands` を列挙し、すべての有効な nested verb が例を含む verb 固有 usage に解決されることを必須とします。さらに destructive な index confirmation、aggregate dependency filter help、suggestion export の read-only GitHub duplicate-preflight 境界を固定します。installer cancellation coverage は固定時間タイマーではなく上限付きの専用 waiter を使い、PID ファイルが完全な正の整数を含むまで待ってから cancel してください。これにより並列負荷下でも redirection によって作成された空ファイルを読みません。
Expand Down
25 changes: 25 additions & 0 deletions changelog.d/unreleased/4882.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
category: fixed
issues:
- 4882
affected:
- src/CodeIndex/Cli/QueryCommandRunner.Graph.cs
- src/CodeIndex/Cli/QueryCommandRunner.ArgumentParser.cs
- src/CodeIndex/Cli/QueryCommandRunner.cs
- src/CodeIndex/Cli/JsonEnvelopeWrapper.Bounded.cs
- src/CodeIndex/Cli/CliFlagSchema.cs
- tests/CodeIndex.Tests/QueryCommandRunnerGraphTests.cs
- tests/CodeIndex.Tests/JsonEnvelopeWrapperIssue4882Tests.cs
- tests/CodeIndex.Tests/QueryCommandRunnerTests.cs
- tests/CodeIndex.Tests/ConsoleUiTests.cs
- DEVELOPER_GUIDE.md
- TESTING_GUIDE.md
---

## English

- **Graph queries no longer silently ignore explicit snippet lengths (#4882)** — `references`, `callers`, and `callees` now reject `--snippet-lines` unless `--body` and text or JSON result output make the requested snippet visible.

## 日本語

- **graph query が明示的な snippet 長を黙って無視しないようになりました (#4882)** — `references`、`callers`、`callees` は、要求した snippet を表示できる `--body` と text または JSON の結果出力を併用しない限り、`--snippet-lines` を拒否するようになりました。
2 changes: 1 addition & 1 deletion src/CodeIndex/Cli/CliFlagSchema.cs
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ private static IReadOnlyList<CliFlag> BuildAll()
new() { Name = "--no-progress", Description = "Disable animated progress and spinner output", PrimaryCommands = Set(AllCommands.ToArray()), TopLevel = true },
new() { Name = "--name", ValuePlaceholder = "<name>", Description = "Exact symbol name", PrimaryCommands = Set("symbols") },
new() { Name = "--max-line-width", ValuePlaceholder = "<n>", Description = "Clamp long single-line payloads (0 disables clamping)", PrimaryCommands = Set(MaxLineWidthCommands) },
new() { Name = "--snippet-lines", ValuePlaceholder = "<n>", Description = "Snippet length; issue-drafts accept 0 for path/line-only evidence", PrimaryCommands = Set("search", "audit", "find", "references", "callers", "callees", "impact") },
new() { Name = "--snippet-lines", ValuePlaceholder = "<n>", Description = "Snippet length; graph queries require --body with text/JSON output; issue-drafts accept 0 for path/line-only evidence", PrimaryCommands = Set("search", "audit", "find", "references", "callers", "callees", "impact") },
new() { Name = "--snippet-focus", ValuePlaceholder = "<leftmost|quality|proximity>", Description = "Search snippet long-line focus mode", PrimaryCommands = Set("search") },
new() { Name = "--fts", Description = "Raw FTS5 syntax; incompatible with search exact/literal modes", PrimaryCommands = Set("search") },
new() { Name = "--no-dedup", Description = "Show duplicate chunks", PrimaryCommands = Set("search") },
Expand Down
41 changes: 40 additions & 1 deletion src/CodeIndex/Cli/JsonEnvelopeWrapper.Bounded.cs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,11 @@ private static int RunBoundedResponse(
CommandExitCodes.UsageError,
controls.MaxJsonBytes);
}
var bodyProjected = HasExplicitBodyProjection(controls.Fields);
var bodyOutputHidden = !bodyProjected
&& (controls.Compact || controls.Fields is { Count: > 0 });
if (!QueryCommandRunner.TryValidateBoundedGraphSnippetLinesOption(command, args, bodyOutputHidden))
return CommandExitCodes.UsageError;
if (HasArgument(args, "--count")
|| command == "find" && IsFindCountResponseRequest(args))
return WriteBoundedResponseUsageError("Bounded response controls cannot be combined with --count.", "Run --count --json separately for a count-only response, or remove --count to page projected rows.");
Expand Down Expand Up @@ -314,7 +319,9 @@ private static int RunBoundedResponse(
extraction.PrimaryCollection))
.ToList();

var count = executionContext?.ReportedTotalCount is { } reportedTotalCount
var count = exitCode == CommandExitCodes.UsageError
? new ResponseCount(controls.Offset + pageItems.Count, false)
: executionContext?.ReportedTotalCount is { } reportedTotalCount
? new ResponseCount(
reportedTotalCount,
executionContext.ReportedTotalCountAuthoritative)
Expand Down Expand Up @@ -1167,6 +1174,7 @@ private static string[] PrepareCountArgs(string command, string[] args)
stripped.RemoveAll(arg => string.Equals(arg, "--body", StringComparison.Ordinal)
|| string.Equals(arg, "--summary-only", StringComparison.Ordinal)
|| string.Equals(arg, "--strict-not-found", StringComparison.Ordinal));
RemoveParsedGraphSnippetLinesOption(stripped);
if (command == "impact")
{
stripped.Add("--limit");
Expand All @@ -1177,6 +1185,37 @@ private static string[] PrepareCountArgs(string command, string[] args)
return [.. stripped];
}

private static void RemoveParsedGraphSnippetLinesOption(List<string> args)
{
for (var i = 0; i < args.Count;)
{
var arg = args[i];
if (string.Equals(arg, "--", StringComparison.Ordinal))
{
i += i + 1 < args.Count ? 2 : 1;
continue;
}
if (arg is "--query" or "--db" or "--path")
{
i += i + 1 < args.Count ? 2 : 1;
continue;
}
if (arg.StartsWith("--snippet-lines=", StringComparison.Ordinal))
{
args.RemoveAt(i);
continue;
}
if (string.Equals(arg, "--snippet-lines", StringComparison.Ordinal))
{
args.RemoveAt(i);
if (i < args.Count)
args.RemoveAt(i);
continue;
}
i++;
}
}

private static List<string> StripResponseOptions(string[] args, bool stripLimit)
{
var stripped = new List<string>(args.Length);
Expand Down
1 change: 1 addition & 0 deletions src/CodeIndex/Cli/QueryCommandRunner.ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ private QueryCommandOptions BuildOptions(DbPathResolution dbResolution, string r
FocusColumn = focusColumn,
FocusLength = focusLength,
SnippetLines = snippetLines,
SnippetLinesExplicit = snippetLinesExplicit,
SnippetFocus = snippetFocus,
MaxLineWidth = maxLineWidth,
PathPatterns = pathPatterns,
Expand Down
Loading
Loading