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 TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ Use `docs/test-doc-maintenance-plan.md` before moving oversized suites or adding
Search raw-FTS/literal-mode conflict coverage shares one pre-dispatch case table across flag ordering, exact aliases, count/issue-draft output, and human/JSON errors; successful issue-draft replay must preserve only its selected matching mode.
Symbols compact flag/alias and summary-only JSON envelopes share one editor-format fixture.
Symbols JSON array, LSP, quickfix, and SARIF location formats share one editor-format fixture; definition SARIF severity coverage reuses that fixture and asserts informational `note` output separately from warning-level diagnostic output.
Validate JSON, compact, count, and SARIF pagination/severity coverage shares one mixed informational/actionable fixture so authoritative totals, limited rows, SARIF levels, and actionability metadata cannot drift across formats; keep missing-`file_issues` degradation coverage in a separate legacy-schema fixture because availability is a distinct mutable state.
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.
Expand Down Expand Up @@ -1174,7 +1174,7 @@ dotnet test --filter "FullyQualifiedName~GitHelperTests"
search の raw-FTS/literal-mode conflict coverage は、flag 順序、exact alias、count/issue-draft output、human/JSON error を1つの pre-dispatch case table で共有してください。成功する issue-draft replay は選択した一致 mode だけを保持することも検証します。
symbols compact flag/aliasとsummary-only JSON envelopeは1つのeditor-format fixtureを共有してください。
symbols JSON array、LSP、quickfix、SARIF location format は1つの editor-format fixture を共有し、definition SARIF severity のテストも同じ fixture を再利用して、情報レベルの `note` 出力を warning レベルの診断出力とは分けて検証してください。
validate の JSON、compact、count、SARIF における pagination / severity coverage は、informational finding と actionable finding が混在する1つの fixture を共有し、authoritative な総件数、limited row、SARIF level、actionability metadata が format 間で drift しないことを検証してください。`file_issues` 欠落時の degradation coverage availability が独立した mutable state なので、別の legacy-schema fixture に分けてください。
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 も扱ってください。
Expand Down
23 changes: 21 additions & 2 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -656,6 +656,7 @@ cdidx validate --kind replacement_char --severity warning --path src/
cdidx validate --exclude-tests --exclude-path 'fixtures/**'
cdidx validate --json=array --limit 50 --path legacy/
cdidx validate --json --limit 50 --path legacy/
cdidx validate --format count --kind replacement_char --path src/
cdidx validate --format compact --limit 50
cdidx validate --format sarif --limit 50
```
Expand All @@ -678,7 +679,17 @@ The default JSON object and `--format compact` expose authoritative pagination
metadata as `returned`, `total`, `omitted`, and `truncated`. Their `count` is the
number of emitted issue rows, while `summary` is computed over all matching
issues before `--limit` and is grouped by kind, severity, origin, category, and
actionability. SARIF exposes the same pagination fields under each run's
actionability. `--format count` emits the common versioned count envelope:
`count` covers all matching validation issues before `--limit`, while
`query_context` records filters such as path, kind, and severity. The legacy
`total_estimated` field remains as a compatibility mirror of `count`.
`api_version`, freshness fields, `issues_table_available`,
`file_issues_data_current`, `severity_filter_available`,
`requested_filters_available`, `index_complete`, `degraded`, and
`authoritative_count` make old databases, unavailable requested filters, and
incomplete indexes explicit;
do not treat the count as exact when `authoritative_count` is `false`.
SARIF exposes the same pagination fields under each run's
`properties`, together with `issues_table_available` and `degraded` so unavailable
legacy validation data is not mistaken for an authoritative zero; each result maps `info` to `note`, preserves `warning` / `error`,
and carries the original `severity`, `origin`, `category`, and `actionable`
Expand Down Expand Up @@ -4005,6 +4016,7 @@ cdidx validate --kind replacement_char --severity warning --path src/
cdidx validate --exclude-tests --exclude-path 'fixtures/**'
cdidx validate --json=array --limit 50 --path legacy/
cdidx validate --json --limit 50 --path legacy/
cdidx validate --format count --kind replacement_char --path src/
cdidx validate --format compact --limit 50
cdidx validate --format sarif --limit 50
```
Expand All @@ -4024,7 +4036,14 @@ validation issue row には `category` と `actionable` も入り、想定済み
`test_fixture` が付きます。既定の JSON object と `--format compact` には、authoritative な
pagination metadata として `returned`、`total`、`omitted`、`truncated` が入ります。`count` は
実際に出力した issue row 数で、`summary` は `--limit` を適用する前の全 matching issue を対象に
kind、severity、origin、category、actionability ごとに集計します。SARIF では同じ pagination field が
kind、severity、origin、category、actionability ごとに集計します。`--format count` は共通の
versioned count envelope を出力します。`count` は `--limit` を適用する前の全 matching validation
issue を対象とし、`query_context` には path、kind、severity などの filter が記録されます。従来の
`total_estimated` は `count` の互換 mirror として維持されます。`api_version`、freshness field、
`issues_table_available`、`file_issues_data_current`、`severity_filter_available`、
`requested_filters_available`、`index_complete`、`degraded`、`authoritative_count` によって
旧 database、利用できない指定 filter、incomplete index を明示するため、
`authoritative_count` が `false` の count を exact として扱わないでください。SARIF では同じ pagination field が
各 run の `properties` に入り、`issues_table_available` と `degraded` も併記されるため、利用できない
legacy validation data が authoritative な 0 件と誤認されることはありません。各 result は `info` を `note` に mapping し、`warning` / `error` は
維持したうえで、元の `severity`、`origin`、`category`、`actionable` を result properties に保持します。
Expand Down
22 changes: 22 additions & 0 deletions changelog.d/unreleased/4908.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
category: fixed
issues:
- 4908
affected:
- src/CodeIndex/Cli/QueryCommandRunner.ResultEnvelopes.cs
- src/CodeIndex/Cli/QueryCommandRunner.Validate.cs
- src/CodeIndex/Database/DbReader.cs
- src/CodeIndex/Database/DbReader.IndexGenerationReadiness.cs
- tests/CodeIndex.Tests/QueryCommandRunnerValidateTests.cs
- tests/CodeIndex.Tests/QueryCommandRunnerValidateMetadataTests.cs
- USER_GUIDE.md
- TESTING_GUIDE.md
---

## English

- **`validate --format count` now uses the common versioned count envelope (#4908)** — count output preserves the legacy `total_estimated` mirror and pre-limit matching semantics while adding API version, filter scope and availability, freshness, index and validation-data readiness, and explicit authoritative/degraded signals.

## 日本語

- **`validate --format count` が共通の versioned count envelope を使うようになりました (#4908)** — 従来の `total_estimated` mirror と limit 適用前の matching semantics を維持しながら、API version、filter scope / availability、freshness、index / validation data の readiness、明示的な authoritative / degraded signal を追加しました。
4 changes: 3 additions & 1 deletion src/CodeIndex/Cli/QueryCommandRunner.ResultEnvelopes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ private static JsonObject BuildQueryContextJson(QueryCommandOptions options, Jso
query["lang"] = options.Lang;
if (options.Kind != null)
query["kind"] = options.Kind;
if (options.Severity != null)
query["severity"] = options.Severity;
if (options.UnusedBucket != null)
query["bucket"] = options.UnusedBucket;
if (options.MinUnusedConfidence != null)
Expand Down Expand Up @@ -261,7 +263,7 @@ private static JsonObject BuildQueryContextJson(QueryCommandOptions options, Jso
query["generated_file_filter_available"] = generatedFileFilterAvailable.Value;
if (options.Since.HasValue)
query["since"] = options.Since.Value;
if (options.CountOnly)
if (options.CountOnly || options.OutputFormat == OutputFormatCount)
query["count"] = true;
if (options.FirstPerFile || options.SampleSize.HasValue)
query["row_selectors"] = BuildSearchRowSelectorContextJson(options);
Expand Down
44 changes: 42 additions & 2 deletions src/CodeIndex/Cli/QueryCommandRunner.Validate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,52 @@ public static int RunValidate(string[] cmdArgs, JsonSerializerOptions jsonOption
var issues = issueLimit.HasValue
? allIssues.Take(issueLimit.Value).ToList()
: allIssues;
var issuesAvailable = reader._hasIssuesTable;
if (options.CountOnly || options.OutputFormat == OutputFormatCount)
{
WriteFormattedCount(allIssues.Count, jsonOptions);
var issuesTableAvailable = reader._hasIssuesPhysicalTable;
var fileIssuesDataCurrent = reader._hasIssuesTable;
var severityFilterAvailable = reader.HasIssueSeverityColumn;
var requestedFiltersAvailable = options.Severity == null || severityFilterAvailable;
var indexCompletion = reader.GetPersistedIndexCompletion();
var payload = BuildCountJsonPayload(
reader,
jsonOptions,
allIssues.Count,
queryOptions: options,
degraded: !issuesTableAvailable
|| !fileIssuesDataCurrent
|| !requestedFiltersAvailable
|| !indexCompletion.IndexComplete,
extraFields: countPayload =>
{
// Kept for compatibility with the pre-envelope count shape (#4908).
countPayload["total_estimated"] = allIssues.Count;
countPayload["count_kind"] = "validation_issues";
countPayload["count_scope"] = "all_matching_issues_before_limit";
countPayload["issues_table_available"] = issuesTableAvailable;
countPayload["file_issues_data_current"] = fileIssuesDataCurrent;
countPayload["severity_filter_available"] = severityFilterAvailable;
countPayload["requested_filters_available"] = requestedFiltersAvailable;
if (!requestedFiltersAvailable)
{
countPayload["requested_filter_unavailable_reasons"] = new JsonArray
{
"severity_column_missing",
};
}
countPayload["index_complete"] = indexCompletion.IndexComplete;
if (!indexCompletion.IndexComplete)
{
countPayload["index_incomplete_reasons"] = JsonSerializer.SerializeToNode(
indexCompletion.IndexIncompleteReasons.ToList(),
CliJsonSerializerContextFactory.Create(jsonOptions).ListString);
}
});
AddActiveSqliteDiagnostics(payload);
CommandOutputWriter.WriteJsonNode(payload, jsonOptions);
return CommandExitCodes.Success;
}
var issuesAvailable = reader._hasIssuesTable;
if (issues.Count == 0)
{
if (options.Json)
Expand Down
Loading
Loading