Skip to content
4 changes: 2 additions & 2 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
Validate JSON, compact, count, and SARIF pagination/severity coverage shares one mixed informational/actionable fixture so authoritative totals, limited rows, SARIF levels, actionability metadata, and the count envelope's API version, filter scope, readiness, and legacy total mirror cannot drift across formats; keep missing-`file_issues` count/SARIF degradation and missing-severity-column filtered-count authority coverage in separate legacy-schema fixtures because table and filter availability are distinct mutable states.
Command-specific output format coverage uses a command/format matrix that checks both parser acceptance and the matching usage line; recognized shared formats without a command implementation need a separate usage-error assertion.
Ad-hoc search SARIF completion coverage shares one fixture across complete, 1-of-126 limited, facet-filtered occurrence-expanded limited, bounded guarded, empty, and synthetically merged multi-run documents. Assert source/emitted/omitted counts and source-count authority in SARIF result units, applied limits, conservative truncation, null cursor state, raw-FTS and option-like-query replay commands, guard-preserving replay, and unchanged rule/location/severity fields on every run.
Recipe SARIF coverage must assert bounded result counts, `recipe/query` rule identity, source locations, severity mapping, confidence, conservative truncation metadata, and stable `fingerprints.cdidx/v1` values across identical runs. Byte-budget coverage must count the complete UTF-8 stdout including JSON escaping and the final newline, exercise exact-fit and one-byte-under boundaries, Unicode, empty and multi-query runs, an individually oversized result, captured/redirected stdout, and replay metadata. Every successful output must parse as complete SARIF, omit only whole results, retain matching rules and locations, and stay within the requested cap. Below-minimum failures must emit no SARIF; non-explicit JSON failures leave stdout empty, while explicit `--json` may emit a bounded versioned error object. Also cover counting-writer measurement and replay recovery when the complete size exceeds the maximum accepted byte cap.
Recipe SARIF coverage must assert bounded result counts, `recipe/query` rule identity, source locations, severity mapping, confidence, conservative truncation metadata, stable `fingerprints.cdidx/v1` values across identical runs, and the same `query_freshness` run properties as aggregate JSON. Query-freshness coverage must keep successful matched and zero-match executions separate from stale index/recipe/query versions and invalid or missing child executions, preserve the compatibility cardinality fields, and reconcile clean/stale/invalid state counts in mixed runs. Byte-budget coverage must count the complete UTF-8 stdout including JSON escaping and the final newline, exercise exact-fit and one-byte-under boundaries, Unicode, empty and multi-query runs, an individually oversized result, captured/redirected stdout, and replay metadata. Every successful output must parse as complete SARIF, omit only whole results, retain matching rules and locations, and stay within the requested cap. Below-minimum failures must emit no SARIF; non-explicit JSON failures leave stdout empty, while explicit `--json` may emit a bounded versioned error object. Also cover counting-writer measurement and replay recovery when the complete size exceeds the maximum accepted byte cap.
MCP schema-origin coverage keeps identical audit phrases in `McpToolCatalog.cs` top-level tool descriptions, concatenated description segments, nested schema-property prose, and executable C# in one indexed fixture; assert explicit `schema_description` search metadata and the recipe's JSON, SARIF, and issue-draft outputs so origin filtering cannot drift across projections.
Recipe row-selection coverage reuses one multi-file, multi-chunk fixture across aggregate JSON, compact JSON, NDJSON, and issue-draft source metadata. Assert emitted/matched/omitted counts, `selection_reason` / `selection_omitted_count`, first-per-file path uniqueness, selector-preserving replay commands, suppressed raw cursors when a later limit truncates selected rows, and rejection of incoming cursors with either selector. A separate candidate-window fixture must exceed the default low-limit fetch envelope and prove that `--sample <n>` observes at least its requested candidate target; validate rejected selectors for non-row recipe shapes without opening a database.
Unused default-suppression row, JSON count, summary-only, and text count envelopes, including the `--all` count control, share one unused-symbol fixture.
Expand Down Expand Up @@ -1184,7 +1184,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
validate の JSON、compact、count、SARIF における pagination / severity coverage は、informational finding と actionable finding が混在する1つの fixture を共有し、authoritative な総件数、limited row、SARIF level、actionability metadata、および count envelope の API version、filter scope、readiness、legacy total mirror が format 間で drift しないことを検証してください。`file_issues` 欠落時の count / SARIF degradation coverage と severity column 欠落時の filtered count authority coverage は、table と filter の availability が別々の mutable state なので、独立した legacy-schema fixture に分けてください。
コマンド別の出力形式 coverage は command / format matrix で parser の受理と対応する usage line の両方を検証してください。共通 parser が認識してもコマンド側に実装がない形式には、別途 usage error の assertion が必要です。
ad-hoc search SARIF の completion coverage は complete、1-of-126 の limited、facet filter 付き occurrence 展開後の limited、bounded guard、empty、合成した multi-run document で1つの fixture を共有します。SARIF result 単位の source / emitted / omitted count と source count の確定性、適用済み limit、保守的な truncation、null cursor state、raw FTS と option のような query の replay command、guard を保持する replay、および各 run で rule / location / severity field が不変であることを検証してください。
Recipe SARIF coverage では、上限付き result count、`recipe/query` rule identity、source location、severity mapping、confidence、保守的な truncation metadata、同一 run 間で安定する `fingerprints.cdidx/v1` を検証してください。byte-budget coverage では JSON escape と末尾改行を含む完全な UTF-8 stdout を数え、exact-fit と1 byte不足の境界、Unicode、空 run と複数 query の run、単体で oversized な result、capture / redirect した stdout、replay metadata を扱ってください。成功した出力はすべて完全な SARIF として parse でき、result を1件単位でのみ省略し、対応する rule / location を維持し、要求 cap 以下でなければなりません。最小値未満の失敗では SARIF を出力せず、明示 JSON でない失敗は stdout を空にし、明示的な `--json` では上限内の version 付き error object を出力できることも検証してください。counting writer による計測と、完全な size が受理可能な最大 byte cap を超える場合の replay recovery も扱ってください。
Recipe SARIF coverage では、上限付き result count、`recipe/query` rule identity、source location、severity mapping、confidence、保守的な truncation metadata、同一 run 間で安定する `fingerprints.cdidx/v1`、aggregate JSON と同じ `query_freshness` run properties を検証してください。query freshness coverage では、成功した matched / zero-match execution を stale な index / recipe / query version および invalid / missing child execution と分離し、互換用の件数フィールドを維持し、mixed run の clean / stale / invalid state count が整合することを検証してください。byte-budget coverage では JSON escape と末尾改行を含む完全な UTF-8 stdout を数え、exact-fit と1 byte不足の境界、Unicode、空 run と複数 query の run、単体で oversized な result、capture / redirect した stdout、replay metadata を扱ってください。成功した出力はすべて完全な SARIF として parse でき、result を1件単位でのみ省略し、対応する rule / location を維持し、要求 cap 以下でなければなりません。最小値未満の失敗では SARIF を出力せず、明示 JSON でない失敗は stdout を空にし、明示的な `--json` では上限内の version 付き error object を出力できることも検証してください。counting writer による計測と、完全な size が受理可能な最大 byte cap を超える場合の replay recovery も扱ってください。
MCP schema-origin coverage では、同一の audit phrase を `McpToolCatalog.cs` の top-level tool description、連結された description segment、nested schema property の prose、実行可能な C# に置いた1つの indexed fixture を共有し、明示的な `schema_description` 検索 metadata と recipe の JSON、SARIF、issue-draft 出力を検証して、projection 間で origin filter が drift しないようにしてください。
recipe row-selection coverage は aggregate JSON、compact JSON、NDJSON、issue-draft の source metadata で1つの multi-file / multi-chunk fixture を共有します。emitted / matched / omitted count、`selection_reason` / `selection_omitted_count`、first-per-file の path uniqueness、selector を保持する replay command、後続 limit が選択済み row を truncate する場合の raw cursor 抑止、両 selector と受け取った cursor の併用拒否を検証してください。別の candidate-window fixture では既定の low-limit fetch envelope を超え、`--sample <n>` が少なくとも要求 candidate 数を観測することを証明し、row を持たない recipe shape での selector 拒否は database を開かずに確認してください。
unused default-suppressionのrow、JSON count、summary-only、text count envelopeは、`--all` count controlも含めて1つのunused-symbol fixtureを共有してください。
Expand Down
33 changes: 23 additions & 10 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -1486,12 +1486,19 @@ include `classifier_counts` when classified rows are present. For example,
`phrase-risk-patterns/task-result-property-review` separates DTO/result-wrapper
`.Result` properties from Task/ValueTask blocking waits. Recipe JSON and
compact output also return `next_cursor` when a single selected recipe query is
truncated. Recipe run summaries and
count summaries include `query_freshness` with the number of queries that found
positive evidence, the number that returned zero results, and `stale_query_names`
so broad audit recipes can surface query drift without requiring the full
recipe catalog JSON. Output-limited recipe runs use matched-count metadata for
this summary, so queries with known omitted matches are not reported as stale.
truncated. Recipe run summaries and count summaries include `query_freshness`.
The compatibility fields `positive_evidence_query_count` and
`zero_result_query_count` still describe result cardinality, while
`clean_query_count`, `matched_query_count`, `clean_zero_match_query_count`, and
per-query `freshness_state` / `result_state` keep successful zero-match queries
separate from real freshness invalidation. `stale_query_names` is now reserved
for stale index or changed recipe/query definition versions; failed or missing
child executions are listed under `invalid_query_names`. Recipe definition and
query definition versions make cached consumers able to detect drift without
inferring freshness from match counts. Text recipe output summarizes the same
states, and SARIF recipe runs expose the same `query_freshness` object in run
properties. Output-limited recipe runs use matched-count metadata for this
summary, so queries with known omitted matches are not reported as zero-match.
Add `--show-excluded` to a recipe run when you need the effective path scope and
exclusion diagnostics in JSON output.
Recipe runs support text output, aggregate JSON with `--json` / `--format json`,
Expand Down Expand Up @@ -4866,10 +4873,16 @@ DTO / result-wrapper の `.Result` property と Task / ValueTask の blocking wa
recipe の JSON / compact output は、単一の recipe query が truncated された場合に `next_cursor`
も返します。`--format compact` は
summary、query count、query ごとの count、`truncated` flag、該当する場合の `next_cursor`
を返します。recipe run summary と count summary は `query_freshness` も返し、肯定的な根拠が
見つかった query 数、結果 0 件の query 数、`stale_query_names` を示します。これにより、
広範な audit recipe の query drift を full recipe catalog JSON なしで確認できます。出力制限された
recipe run では matched-count metadata を使うため、省略済みの match がある query は stale として報告されません。
を返します。recipe run summary と count summary は `query_freshness` も返します。
互換フィールドの `positive_evidence_query_count` と `zero_result_query_count` は引き続き結果件数を
表し、`clean_query_count`、`matched_query_count`、`clean_zero_match_query_count` と query ごとの
`freshness_state` / `result_state` により、正常に実行された 0 件の query を実際の freshness
無効化と分離します。`stale_query_names` は stale index または recipe / query definition version
変更だけに使い、失敗または欠落した child execution は `invalid_query_names` に示します。
recipe definition と query definition の version により、cache consumer は match 数から freshness
を推測せず drift を検出できます。text の recipe output も同じ状態を要約し、SARIF recipe run は
run properties の `query_freshness` に同じ object を出力します。出力制限された recipe run では
matched-count metadata を使うため、省略済みの match がある query は zero-match として報告されません。
`--show-excluded` を recipe と併用すると、有効な path scope と除外診断を出力に含めます。
recipe run が対応する形式は text output、`--json` / `--format json` の aggregate JSON、
`--json=ndjson` または `--results-only` の NDJSON row stream、`--format count` の
Expand Down
21 changes: 21 additions & 0 deletions changelog.d/unreleased/4907.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
category: fixed
issues:
- 4907
affected:
- src/CodeIndex/Cli/JsonOutputContracts.cs
- src/CodeIndex/Cli/QueryCommandRunner.SearchRecipes.cs
- src/CodeIndex/Cli/QueryCommandRunner.SearchResults.cs
- src/CodeIndex/Cli/SearchAuditRecipes.cs
- tests/CodeIndex.Tests/QueryCommandRunnerSearchTests.cs
- USER_GUIDE.md
- TESTING_GUIDE.md
---

## English

- **Fresh named and recipe queries with zero matches are no longer reported as stale (#4907)** — `query_freshness` now separates successful matched and zero-match executions from stale index/definition versions and invalid child executions, while preserving the existing cardinality fields and synchronizing JSON, text, SARIF, and issue-draft metadata.

## 日本語

- **正常に実行されて結果が 0 件だった named query と recipe query を stale として報告しないようにしました (#4907)** — `query_freshness` は成功した match / zero-match execution、stale な index / definition version、無効な child execution を分離し、既存の件数フィールドを維持しながら JSON、text、SARIF、issue-draft metadata を同期します。
2 changes: 2 additions & 0 deletions src/CodeIndex/Cli/JsonOutputContracts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,8 @@ internal sealed record ValidateConfigJsonResult(
[JsonSerializable(typeof(SearchNamedBatchRunJsonResult))]
[JsonSerializable(typeof(SearchNamedBatchCountSummaryQueryJsonResult))]
[JsonSerializable(typeof(SearchNamedBatchCountSummaryRunJsonResult))]
[JsonSerializable(typeof(SearchAuditRecipe))]
[JsonSerializable(typeof(SearchAuditRecipeQuery))]
[JsonSerializable(typeof(SearchRecipeListItemJsonResult))]
[JsonSerializable(typeof(SearchRecipeListJsonResult))]
[JsonSerializable(typeof(SearchRecipeNameListJsonResult))]
Expand Down
Loading
Loading