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: 2 additions & 2 deletions DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1693,7 +1693,7 @@ access.
| Recipe SARIF | `search --recipe <name> --format sarif` emits one result per bounded recipe result. Rule IDs use `recipe/query`; standard `fingerprints.cdidx/v1` values are derived from the normalized source location; result properties preserve recipe/query identity, severity, confidence, and per-query truncation; run properties preserve scope, applied result limits, aggregate counts, and conservative omitted-result metadata. Bound SARIF with `--limit` / `--total-limit`; row selectors such as `--sample`, `--first-per-file`, and `--per-file-limit` are rejected instead of being silently ignored. Recipe severity maps `critical` / `high` to `error`, `medium` to `warning`, and `low` / `info` to `note`. |
| Recipe classifier output | Recipe run JSON may add `audit_classifications` to individual `CompactSearchResult` rows when a recipe classifier can classify the hit, and query/count payloads may add `classifier_counts` when classified rows are present. These fields are additive; use them to separate triage domains such as DTO/result-wrapper `.Result` properties versus Task/ValueTask blocking waits without changing the raw search query. |
| NDJSON terminal records | Default NDJSON for `search`, `symbols`, and `files` appends one final `terminal_record` after result rows; search also emits it for zero-result responses, while raw `symbols` and `files` keep zero-result NDJSON empty. Recipe/audit search row streams share the same writer. Terminals report returned and observed total counts, `total_count_authoritative` / `total_count_lower_bound`, selection or interruption reason, applied limits, omitted rows, and recovery guidance. `--max-json-bytes` covers the complete stdout stream, including newlines and this terminal record; when additive selector-accounting fields prevent the terminal from fitting, the writer omits those optional fields before declaring the terminal impossible. A cap that still cannot fit the terminal fails before stdout. Capped output rejects `--profile`, `--verbose`, and `--json-envelope`. Byte-cap partial output exits with `CommandExitCodes.PartialResult` (`11`) unless `--allow-partial` explicitly opts into exit `0`. `--results-only` is the explicit terminal-record opt-out for these NDJSON row streams and is rejected with array, compact, summary, or count output. |
| `outline` / `unused` cursor binding | `outline --json` accepts `--kind <kind[,kind]>`, `--limit` / `--top`, opaque `--cursor <next_cursor>`, and `--outline-fields <csv>` for bounded machine output. Controlled outline responses keep the normal envelope and add `total_symbol_count`, `returned_symbol_count`, `cursor_offset`, `next_cursor`, `has_more`, and `result_stable_at`, plus `kind_filter` and `selected_fields` when active. `outline` and `unused` cursors bind their offset to the normalized path/scope, filters, ordering, and index generation; reuse after changing those inputs or refreshing the index fails with explicit restart-required guidance. Legacy `outline:<offset>` / `unused:<offset>` inputs remain accepted for transition, but every newly emitted cursor is opaque and bound. |
| `outline` / `unused` cursor binding | `outline --json` accepts `--kind <kind[,kind]>`, `--limit` / `--top`, opaque `--cursor <next_cursor>`, and `--outline-fields <csv>` for bounded machine output. Controlled outline responses keep the normal envelope and add `total_symbol_count`, `returned_symbol_count`, `cursor_offset`, `next_cursor`, `has_more`, and `result_stable_at`, plus `kind_filter` and `selected_fields` when active. Projection parsing canonicalizes aliases and removes duplicates before validation; unknown field names are reported together as one terminal usage error with valid candidates, while the empty-selection error is reserved for deliberately empty CSV input. `outline` and `unused` cursors bind their offset to the normalized path/scope, filters, ordering, and index generation; reuse after changing those inputs or refreshing the index fails with explicit restart-required guidance. Legacy `outline:<offset>` / `unused:<offset>` inputs remain accepted for transition, but every newly emitted cursor is opaque and bound. |
| `hotspots --json` grouping semantics | `hotspots` and MCP `symbol_hotspots` emit `grouped_by`, `grouping_unit`, `count_kind`, `limit_applies_to`, `score_fields`, `ranking_fields`, and matching `query_context` fields. `--limit` applies to returned symbols, files, name/kind groups, or SQL statements; `--count` ignores `--limit` and reports total groups. Explicit `statement` grouping is SQL-only (`--lang sql` / `lang: "sql"`). |
| `--json-envelope` commands | Applies to `search`, `definition`, `references`, `callers`, `callees`, `symbols`, `files`, `find`, `excerpt`, `map`, `inspect`, `outline`, `status`, `validate`, `languages`, `impact`, `deps`, `unused`, and `hotspots`. |
| `--json-envelope` shape | Wraps the per-line `--json` stream into a single `{"metadata": {...}, "results": [...]}` document. Stream terminal records are excluded from `results` and preserved as `metadata.stream_terminal`, while zero-result prelude/control records are preserved as `metadata.stream_control_records`; therefore `result_count` counts result rows only. A `find --all --count` object is both the count result and terminal scan metadata, so it remains in `results` and is also copied to `metadata.stream_terminal`. `metadata` also carries `api_version`, `command`, `cdidx_version`, `elapsed_ms`, `db_path`, `exit_code`, and, when applicable, `query_normalized` and `indexed_at_head_sha`. `indexed_at_head_sha` maps to the persisted latest-successful `indexed_head_sha` used by status and MCP output after full, `--files`, `--commits`, and `--changed-between` refreshes; failed/rolled-back refreshes do not advance it, and legacy DBs without that key fall back to full-scan-only `indexed_head_commit`. The bounded high-volume commands above support `--json-envelope --max-json-bytes` by measuring the final serialized document; other envelope/byte-cap combinations remain rejected. |
Expand Down Expand Up @@ -5060,7 +5060,7 @@ help はすべてこのレジストリを参照します。field 名は大文字
| Recipe SARIF | `search --recipe <name> --format sarif` は、上限付き recipe result ごとに result を1件出力します。rule ID は `recipe/query` を使い、標準の `fingerprints.cdidx/v1` は正規化済み source location から導出します。result properties は recipe/query identity、severity、confidence、query ごとの truncation を保持し、run properties は scope、適用済み result limit、集計 count、保守的な omitted-result metadata を保持します。SARIF の上限には `--limit` / `--total-limit` を使い、`--sample`、`--first-per-file`、`--per-file-limit` のような row selector は黙って無視せず拒否します。recipe severity は `critical` / `high` を `error`、`medium` を `warning`、`low` / `info` を `note` に対応付けます。 |
| Recipe classifier output | recipe classifier が hit を分類できる場合、recipe run JSON は個別の `CompactSearchResult` row に `audit_classifications` を追加することがあり、分類済み row がある query / count payload は `classifier_counts` を追加することがあります。これらは additive field です。raw search query を変えずに、DTO / result-wrapper の `.Result` property と Task / ValueTask の blocking wait などの triage domain を分離するために使います。 |
| NDJSON terminal record | `search`、`symbols`、`files` の既定 NDJSON は result row の後に最後の `terminal_record` を 1 件追加します。`search` は 0 件応答にも終端を出力しますが、raw `symbols` / `files` の 0 件 NDJSON は空のままです。recipe / audit search の row stream も同じ writer を使います。終端は返却件数と観測済み総件数、`total_count_authoritative` / `total_count_lower_bound`、selection または中断理由、適用上限、省略行数、復旧案内を報告します。`--max-json-bytes` は改行と終端レコードを含む stdout stream 全体を対象にし、追加 selector-accounting field が原因で終端が収まらない場合は、終端自体を不可能と判定する前にそれらの任意 field を省略します。それでも終端が収まらない cap は stdout 出力前に失敗します。上限付き出力は `--profile`、`--verbose`、`--json-envelope` を拒否します。byte cap による部分出力は、`--allow-partial` で終了コード `0` を明示許可しない限り `CommandExitCodes.PartialResult`(`11`)を返します。`--results-only` はこれらの NDJSON row stream から終端レコードを明示的に除外するための option であり、array / compact / summary / count 出力との組み合わせは拒否されます。 |
| `outline` / `unused` cursor の束縛 | `outline --json` は bounded な機械向け出力として `--kind <kind[,kind]>`、`--limit` / `--top`、opaque な `--cursor <next_cursor>`、`--outline-fields <csv>` を受け付けます。制御付き outline 応答は通常の envelope を維持し、`total_symbol_count`、`returned_symbol_count`、`cursor_offset`、`next_cursor`、`has_more`、`result_stable_at` を追加し、該当時は `kind_filter` と `selected_fields` も返します。`outline` と `unused` の cursor は offset を正規化済み path/scope、filter、ordering、index generation に束縛するため、条件変更後または index 更新後の再利用は restart-required の明示案内付きで失敗します。移行用に legacy の `outline:<offset>` / `unused:<offset>` 入力は受理しますが、新しく出力する cursor はすべて opaque かつ束縛済みです。 |
| `outline` / `unused` cursor の束縛 | `outline --json` は bounded な機械向け出力として `--kind <kind[,kind]>`、`--limit` / `--top`、opaque な `--cursor <next_cursor>`、`--outline-fields <csv>` を受け付けます。制御付き outline 応答は通常の envelope を維持し、`total_symbol_count`、`returned_symbol_count`、`cursor_offset`、`next_cursor`、`has_more`、`result_stable_at` を追加し、該当時は `kind_filter` と `selected_fields` も返します。projection parser は検証前に alias を canonicalize して重複を除きます。未知field名はvalid候補を伴う1つの終端usage errorにまとめ、empty-selection errorは意図的に空のCSV入力にだけ使います。`outline` と `unused` の cursor は offset を正規化済み path/scope、filter、ordering、index generation に束縛するため、条件変更後または index 更新後の再利用は restart-required の明示案内付きで失敗します。移行用に legacy の `outline:<offset>` / `unused:<offset>` 入力は受理しますが、新しく出力する cursor はすべて opaque かつ束縛済みです。 |
| `hotspots --json` grouping semantics | `hotspots` と MCP `symbol_hotspots` は `grouped_by`、`grouping_unit`、`count_kind`、`limit_applies_to`、`score_fields`、`ranking_fields` と、対応する `query_context` field を返します。`--limit` は返却される symbol、file、name/kind group、SQL statement に適用されます。`--count` は `--limit` を無視し、total group 数を返します。明示的な `statement` grouping は SQL 専用です(`--lang sql` / `lang: "sql"`)。 |
| `--json-envelope` 対象 command | `search`、`definition`、`references`、`callers`、`callees`、`symbols`、`files`、`find`、`excerpt`、`map`、`inspect`、`outline`、`status`、`validate`、`languages`、`impact`、`deps`、`unused`、`hotspots`。 |
| `--json-envelope` shape | per-line `--json` stream を単一の `{"metadata": {...}, "results": [...]}` document に包みます。stream 終端レコードは `results` から除外して `metadata.stream_terminal` に保持し、0 件時の prelude / control record は `metadata.stream_control_records` に保持するため、`result_count` は result row だけを数えます。`find --all --count` object は count result であると同時に終端 scan metadata でもあるため、`results` に残しつつ `metadata.stream_terminal` にも複製します。`metadata` は `api_version`、`command`、`cdidx_version`、`elapsed_ms`、`db_path`、`exit_code`、該当時は `query_normalized` と `indexed_at_head_sha` も持ちます。`indexed_at_head_sha` は full、`--files`、`--commits`、`--changed-between` refresh 後に status / MCP output が使う永続化済みの最新成功 `indexed_head_sha` に対応します。失敗または rollback された refresh では進まず、この key を持たない legacy DB では full-scan 限定 `indexed_head_commit` に fallback します。上記の bounded 高ボリューム command は最終 document を測定することで `--json-envelope --max-json-bytes` を許可し、それ以外の envelope / byte-cap 組み合わせは引き続き拒否します。 |
Expand Down
2 changes: 2 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
Outline `size` sorting and its `span` alias share one ranking fixture.
Outline reference and complexity metric sorting share one derived-ranking fixture.
Outline kind sorting and default source-order field projection share one ranking fixture.
Outline projection validation reuses one parser/JSON-error fixture across single and multiple unknown fields, deliberately empty lists, aliases, duplicates, and mixed valid/invalid input; assert that unknown fields form one terminal usage error and do not trigger the empty-selection diagnostic.
Deps JSON and json-graph byte-limit failures share one SQL graph fixture.
Deps JSON summary output and json-graph summary rejection share one SQL graph fixture.
Dependency-cycle coverage must prove that the graph budget is independent of the display limit, SCC ranking remains stable when the page size grows, opaque cursors return the next ranked component, mismatched cursor filters fail closed, and graph-budget exhaustion marks totals as non-authoritative.
Expand Down Expand Up @@ -1142,6 +1143,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
outlineの`size` sortとその`span` aliasは1つのranking fixtureを共有してください。
outlineのreference metric sortとcomplexity metric sortは1つのderived-ranking fixtureを共有してください。
outlineのkind sortとdefault source-order field projectionは1つのranking fixtureを共有してください。
outline projection validationは、単一・複数の未知field、意図的な空list、alias、重複、valid/invalid混在を1つのparser/JSON-error fixtureで共有してください。未知fieldが1つの終端usage errorを形成し、empty-selection diagnosticを追加で発生させないことを検証してください。
depsのJSONとjson-graphのbyte-limit failureは1つのSQL graph fixtureを共有してください。
depsのJSON summary outputとjson-graph summary rejectionは1つのSQL graph fixtureを共有してください。
dependency-cycle coverage では、graph budget が表示 limit から独立していること、page size を増やしても SCC 順位が安定すること、不透明 cursor が次の順位の component を返すこと、cursor と filter の不一致が fail-closed になること、graph-budget 枯渇時に総件数が non-authoritative と示されることを検証してください。
Expand Down
18 changes: 18 additions & 0 deletions changelog.d/unreleased/4876.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
category: fixed
issues:
- 4876
affected:
- src/CodeIndex/Cli/QueryCommandRunner.ArgParsing.cs
- tests/CodeIndex.Tests/QueryCommandRunnerInspectTests.cs
- DEVELOPER_GUIDE.md
- TESTING_GUIDE.md
---

## English

- **Invalid outline projections now emit one stable usage error (#4876)** — `--outline-fields` collects unknown field names into one terminal error with valid candidates and no longer adds a misleading empty-selection failure after the primary validation error.

## 日本語

- **不正なoutline projectionが安定したusage errorを1件だけ返すようになりました (#4876)** — `--outline-fields` は未知field名をvalid候補付きの1つの終端errorにまとめ、一次validation errorの後に誤解を招くempty-selection failureを追加しなくなりました。
11 changes: 10 additions & 1 deletion src/CodeIndex/Cli/QueryCommandRunner.ArgParsing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ private static List<string> ParseMapSections(string rawValue, Action<string> add
{
var fields = new List<string>();
var seen = new HashSet<string>(StringComparer.Ordinal);
var invalidFields = new List<string>();
var all = false;
if (!ValidateCsvBounds("--outline-fields", rawValue, MaxOutlineProjectionFieldsCsvLength, MaxOutlineProjectionFieldsCsvEntries, addParseError))
return fields;
Expand Down Expand Up @@ -414,11 +415,19 @@ void AddField(string field)
AddField("container_name");
break;
default:
addParseError($"Error: unsupported --outline-fields value '{ConsoleUi.FormatBoundedValue(rawField)}'. Use one or more of all, kind, name, display_name, path, line, start_line, end_line, depth, body_start_line, body_end_line, signature, signature_truncated, signature_original_length, container_kind, container_name, visibility, return_type, sort_mode, reference_count, size_lines, complexity_score, or aliases range, lines, body, body_range, container, refs, size, span, complexity.");
invalidFields.Add(rawField);
continue;
}
}

if (invalidFields.Count > 0)
{
var invalidValues = string.Join(", ", invalidFields.Select(field => $"'{ConsoleUi.FormatBoundedValue(field)}'"));
var valueLabel = invalidFields.Count == 1 ? "value" : "values";
addParseError($"Error: unsupported --outline-fields {valueLabel} {invalidValues}. Use one or more of all, kind, name, display_name, path, line, start_line, end_line, depth, body_start_line, body_end_line, signature, signature_truncated, signature_original_length, container_kind, container_name, visibility, return_type, sort_mode, reference_count, size_lines, complexity_score, or aliases range, lines, body, body_range, container, refs, size, span, complexity.");
return all ? null : fields;
}

if (all && fields.Count > 0)
addParseError("Error: --outline-fields all cannot be combined with specific field names.");
if (!all && fields.Count == 0)
Expand Down
Loading
Loading