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
2 changes: 2 additions & 0 deletions TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,7 @@ Candidate-ordered parallel-index recovery tests must prove that the fatal result
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.
Search-core orchestration coverage must keep validation ahead of database execution, preserve list-recipes > named-query > recipe > plain route precedence and the recipe-field-before-conflict / plain-conflict-before-field aggregation error order, keep count-mode zero/nonzero payloads aligned, retain strict-not-found exit semantics, and order profiled NDJSON as result records, profile metadata, then the terminal record.
Plain search strict-count coverage (#5406) extends the shared #5393 direct/batch fixture and `OriginContinuationIssue5348Tests`. On net8/net9, preserve zero/nonzero numeric and JSON counts, `--format count`, serial/parallel batch classification, snapshot guards, and byte-budget precedence. Filtered unknown origins must remain non-authoritative even with `--allow-partial`; completed classification may establish a strict zero. Keep grouped/named/recipe and MCP count regressions as controls.
`QueryCommandRunnerSearchCancellationTests` (#5421) covers pre-cancelled plain rows/counts/aggregations and cancellation inside real SQLite search statements through direct, CLI and serial/parallel batch entry points. Keep FTS hits, exact misses, numeric/JSON output, strict/allow-partial precedence, native interruption and successful retry on net8/net9. Also verify that C#/Python managed origin classification retains the effective scoped token, reader reuse restores its lifetime token, and cancellation at the first/last prepared aggregation group emits no success. Direct CLI cancellation writes a human error; batch children report `E012_INTERRUPTED`. The console-sensitive fixture restores its async-local connection factory and aggregation seam; callbacks trigger cancellation without sleeps or global pool resets.
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.
Suggestions usage-hint coverage for #5196 must keep unknown-option human diagnostics and missing-id / invalid-combination JSON errors aligned on `cdidx suggestions --help`, preserve the existing error-code, category, usage, and exit-code contracts, and assert that the literal `<cmd>` placeholder is never emitted. `CommandErrorWriterTests.cs` must keep catalog-backed known-command hints and the global fallback for missing, unknown, nested, or control-bearing command identities.
Explicit `--files` preflight coverage for #5091 / #5122 keeps real/dry-run and JSON/text failure behavior in one focused contract matrix: bare `--files`, absolute and relative outside-root paths, platform-supported symlink escapes, `none`-mode directory-segment rejection, `internal`/`all` acceptance, nonexistent, directory, filtered, and unsupported targets, canonical duplicates (including target-specific casing and native Unicode/alias spelling), mixed valid/invalid atomicity, raw-token provenance beside `--project` expansion, already-indexed cleanup and existing or deleted reconciliation-control exceptions, generated-code acceptance, unreadable membership snapshots, and the implicit empty full-scan control without `--files`. Include Windows 8.3 ancestor aliases that must map back to established database spelling. Indexed controls replaced by a directory, FIFO, or policy-disallowed symlink must remain cleanup tombstones without opening or following the replacement. TypeScript path-alias configuration tests must cover `none`, inside/outside-root `internal`, and `all`, prove that only regular resolved targets are secure-opened, and keep real/dry-run plus in-process/worker paths aligned. Assert `UsageError`, bounded and redacted per-input reason diagnostics, and unchanged indexed rows and metadata whenever any explicit token is rejected; snapshot-read failures fail closed with a database error.
Expand Down Expand Up @@ -2382,6 +2383,7 @@ results-only・array・envelope・compact の形状、終端と改行の厳密
YAML `outline` の integration coverage では、入れ子 sequence path、mapping parent の depth、不変な source range と symbol count、決定的な sort、page 境界を越える cursor replay を検証してください。
search core の orchestration coverage では、validation が database execution より先に行われること、list-recipes > named-query > recipe > plain の route precedence と recipe では field error が conflict より先・plain では conflict が field error より先になる aggregation error 順、count mode の zero/nonzero payload、strict-not-found の exit semantics、profile 付き NDJSON が result record、profile metadata、terminal record の順になることを維持してください。
通常検索の strict 件数テスト (#5406) は、#5393 の直接実行/batch 共有 fixture と `OriginContinuationIssue5348Tests` を拡張します。net8/net9 でゼロ件/非ゼロ件の数値・JSON、`--format count`、直列/並列 batch の分類、スナップショットの保護、応答サイズエラーの優先順位を維持してください。フィルターで除外した分類不明の候補は `--allow-partial` 指定時も不在の根拠にせず、分類完了時だけ strict の確定したゼロ件として扱います。grouped/named/recipe と MCP の既存件数テストも対照として実行します。
`QueryCommandRunnerSearchCancellationTests` (#5421) は、通常検索の行・件数・集計の事前取消と、直接実行・CLI・直列/並列バッチでの SQLite 検索文の実行中の取消を検証します。FTS の一致、完全一致検索の未検出、数値/JSON 出力、strict/allow-partial より取消が優先すること、SQLite の中断と再実行の成功を net8/net9 で維持してください。C#/Python の管理コードによる検索元分類がスコープの有効なトークンを保持すること、reader の再利用時に元の寿命トークンを復元すること、最初/最後の集計グループ準備時の取消で成功出力を行わないことも検証します。直接 CLI は人間向けエラーを出し、バッチ内では `E012_INTERRUPTED` を報告します。console-sensitive なフィクスチャは非同期コンテキスト単位の接続ファクトリと集計用テストフックを復元し、待ち時間や全体の接続プール初期化に依存せずコールバックで取消を発生させます。
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 のままであることも検証してください。
#5196 の suggestions usage-hint coverage では、unknown option の human 診断と missing id / invalid combination の JSON error がともに `cdidx suggestions --help` を案内し、既存の error code、category、usage、exit code 契約を維持し、literal `<cmd>` placeholder を出力しないことを固定してください。`CommandErrorWriterTests.cs` では catalog に基づく既知 command の hint と、command identity が欠落・未知・nested・control character を含む場合の global fallback を維持してください。
#5091 / #5122 の明示的な `--files` preflight coverage では、通常実行 / dry-run と JSON / text の failure behavior を 1 つの focused contract matrix に保ちます。path token のない `--files`、absolute / relative の project root 外 path、対応 platform での symlink escape、`none` mode での directory segment symlink の拒否、`internal` / `all` mode での受理、存在しない target、directory、filter 対象、未対応 target、canonical duplicate(target ごとの casing と native Unicode/alias spelling を含む)、valid / invalid 混在時の atomicity、`--project` 展開と並ぶ raw token provenance、既に index 済みの cleanup、既存または削除済みの reconciliation control の例外、generated code の受理、membership snapshot の読取不能、`--files` のない暗黙の空 full scan control を検証してください。Windows の 8.3 ancestor alias は DB に確立済みの spelling へ戻ることを検証してください。indexed control が directory、FIFO、または policy で禁止された symlink に置換された場合は、置換 object を open / follow せず cleanup tombstone を維持する必要があります。TypeScript path alias configuration test は `none`、root 内外の `internal`、`all` を網羅し、解決後の regular target だけが secure-open されることを証明し、通常実行 / dry-run と in-process / worker path を一致させてください。明示 token が 1 件でも拒否された場合は、`UsageError`、上限付き・伏字化済みの入力別 reason diagnostic、indexed row と metadata の不変性を assertion し、snapshot 読取失敗は database error で fail closed することを確認します。
Expand Down
9 changes: 9 additions & 0 deletions USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,11 @@ find counts, incomplete search-origin classification, and potentially stale
snapshots cannot establish absence.
`--allow-partial` accepts partial output without making its zero authoritative;
cancellation, timeout, cursor, and output errors retain their precedence.
Plain search propagates cancellation through database setup and query execution for
rows, counts, and aggregations, including serial and parallel batch children.
Cancelling the CLI returns exit `130`; cancelled batch children also report
`E012_INTERRUPTED`. `--strict-not-found` and `--allow-partial` cannot turn that
cancellation into a miss or success.
Final and zero-result pages do not advertise a cursor. If a partial stream
cannot make safe progress—for example, a byte cap leaves room only for the
terminal record, the query uses row selectors or a recipe/named search, the
Expand Down Expand Up @@ -4709,6 +4714,10 @@ command、query、filter、ordering、page limit を変えずに再利用して
検索元の分類が未完了の場合、古い可能性があるスナップショットでは不在を断定しません。
`--allow-partial` は部分結果を受け入れるだけで 0 件の確実性を高めず、
取消・タイムアウト・カーソル・出力エラーを優先します。
通常検索は、行・件数・集計の各出力で、DB の準備から検索の実行まで取消を伝播します。
直列・並列バッチ内の検索も対象です。CLI の取消は終了コード `130` を返し、
バッチ内の取消では `E012_INTERRUPTED` も報告します。`--strict-not-found` や
`--allow-partial` を指定しても、取消を未検出や成功には変換しません。
最終 page と 0 件 page は cursor を公開しません。byte cap により terminal
record しか出力できない場合、row selector または recipe / named search を使う場合、
10,000 row の pagination window を使い切ったか、同じ page limit での再開時にその上限を
Expand Down
20 changes: 20 additions & 0 deletions changelog.d/unreleased/5421.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
category: fixed
issues:
- 5421
affected:
- src/CodeIndex/Cli/QueryCommandRunner.SearchRouting.cs
- src/CodeIndex/Cli/QueryCommandRunner.SearchExecution.cs
- src/CodeIndex/Cli/QueryCommandRunner.SearchResults.cs
- src/CodeIndex/Database/DbSearchReader.cs
- src/CodeIndex/Database/DbSearchReader.PythonOrigins.cs
- src/CodeIndex/Database/DbSearchReader.SymbolGuards.cs
---

## English

- **Plain search now honors caller cancellation (#5421)** — cancellation reaches database setup, running SQLite queries, managed origin classification and aggregation for rows and counts, including batch execution. Cancellation remains an interruption instead of becoming a strict zero-result exit or an accepted partial success.

## 日本語

- **通常検索が呼出元の取消に従うよう修正 (#5421)** — バッチ内の検索を含め、行・件数の DB 準備、実行中の SQLite 検索、管理コード側の検索元分類と集計に取消を伝播します。取消を strict のゼロ件終了や許容された部分成功へ変換せず、中断として扱います。
21 changes: 16 additions & 5 deletions src/CodeIndex/Cli/QueryCommandRunner.SearchExecution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ private sealed record SearchExecutionPlan(
JsonSerializerOptions NdjsonOptions,
bool ExactSearch,
string Query,
SearchQueryHint? ExactSubstringHint);
SearchQueryHint? ExactSubstringHint,
CancellationToken CancellationToken);

private sealed record SearchRowExecution(
FtsQueryDiagnostics FtsQueryDiagnostics,
Expand All @@ -31,7 +32,8 @@ private static SearchExecutionPlan CreateSearchExecutionPlan(
QueryCommandOptions options,
JsonSerializerOptions jsonOptions,
bool exactSearch,
string query)
string query,
CancellationToken cancellationToken)
{
var exactSubstringHint = SearchQueryAdvisor.BuildExactSubstringHint(
query,
Expand All @@ -47,17 +49,24 @@ private static SearchExecutionPlan CreateSearchExecutionPlan(
ndjsonOptions,
exactSearch,
query,
exactSubstringHint);
exactSubstringHint,
cancellationToken);
}

private static int ExecutePlainSearch(SearchExecutionPlan plan)
{
plan.CancellationToken.ThrowIfCancellationRequested();
var outcome = new SearchExecutionOutcome();
return WithDb(
plan.Options,
plan.JsonOptions,
reader => ExecutePlainSearch(reader, plan, outcome),
_ => WritePlainSearchTerminal(plan, outcome));
reader =>
{
using var cancellationScope = reader.BeginCancellationScope(plan.CancellationToken);
return reader.RunWithCancellationInterrupt(() => ExecutePlainSearch(reader, plan, outcome));
},
_ => WritePlainSearchTerminal(plan, outcome),
cancellationToken: plan.CancellationToken);
}

private static int ExecutePlainSearch(
Expand Down Expand Up @@ -88,6 +97,7 @@ private static int ExecutePlainSearch(
return WritePlainSearchCount(reader, plan);

var rows = PreparePlainSearchRows(reader, plan);
plan.CancellationToken.ThrowIfCancellationRequested();
var exitCode = rows.DisplayRows.Count == 0
? WriteEmptyPlainSearchResults(reader, plan, rows, outcome)
: WritePlainSearchResults(reader, plan, rows, outcome);
Expand All @@ -101,6 +111,7 @@ private static int WritePlainSearchCount(DbReader reader, SearchExecutionPlan pl
var options = plan.Options;
var originCoverage = new SearchCountOriginCoverage(options);
var counts = CountSearchMatches(reader, options, plan.ExactSearch, originCoverage);
plan.CancellationToken.ThrowIfCancellationRequested();
var queryDiagnostics = DbReader.AnalyzeFtsQuery(
plan.Query,
options.RawFts,
Expand Down
Loading
Loading