diff --git a/changelog.d/unreleased/2687.fixed.md b/changelog.d/unreleased/2687.fixed.md new file mode 100644 index 0000000000..c8339dfbac --- /dev/null +++ b/changelog.d/unreleased/2687.fixed.md @@ -0,0 +1,19 @@ +--- +category: fixed +issues: + - 2687 + - 2689 + - 2690 + - 2693 +affected: + - src/CodeIndex/Cli/IndexCommandRunner.cs + - tests/CodeIndex.Tests/IndexCommandRunnerTests.cs +--- + +## English + +- **Indexing no longer stalls indefinitely on pathological symbol extraction (#2687, #2689, #2690, #2693)** - `cdidx index` now bounds symbol extraction progress and reports `E013_INDEX_EXTRACTION_STALLED` instead of hanging or misreporting the run as a user interrupt. + +## 日本語 + +- **病的に遅いシンボル抽出で index が無期限に停止しないようになりました (#2687, #2689, #2690, #2693)** - `cdidx index` はシンボル抽出の進捗停止を制限し、ハングしたりユーザー割り込みとして誤報したりせず `E013_INDEX_EXTRACTION_STALLED` を報告します。 diff --git a/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs b/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs index c98fd4d21b..921a893602 100644 --- a/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs +++ b/tests/CodeIndex.Tests/IndexCommandRunnerTests.cs @@ -70,6 +70,39 @@ public void FormatIndexPhasePath_AppendsPhaseSuffixForJsonLiveness() Assert.Equal("src/App.cs (references)", message); } + [Fact] + public void Run_FilesMode_WhenSymbolExtractionStalls_ReportsStallInsteadOfInterrupt() + { + var priorTimeout = IndexCommandRunner.IndexExtractionStallTimeoutForTesting; + IndexCommandRunner.IndexExtractionStallTimeoutForTesting = () => TimeSpan.FromMilliseconds(1); + var projectRoot = CreateTempProject(); + try + { + var source = Path.Combine( + GetRepositoryRoot(), + "src", + "CodeIndex", + "Indexer", + "Symbols", + "SymbolExtractor.JavaScriptTypeScriptSupport.cs"); + File.Copy(source, Path.Combine(projectRoot, "slow.cs")); + + var dbPath = Path.Combine(Path.GetTempPath(), $"cdidx_symbol_timeout_{Guid.NewGuid():N}.db"); + var (exitCode, json, stderr) = RunAndCaptureJsonWithStderr([projectRoot, "--files", "slow.cs", "--db", dbPath, "--json", "--force"]); + + Assert.Equal(CommandExitCodes.CancelledBySignal, exitCode); + Assert.Equal(CommandErrorCodes.IndexExtractionStalled, json.GetProperty("error_code").GetString()); + Assert.Contains("Index extraction made no progress", json.GetProperty("message").GetString()); + Assert.DoesNotContain(CommandErrorCodes.Interrupted, stderr); + } + finally + { + IndexCommandRunner.IndexExtractionStallTimeoutForTesting = priorTimeout; + SqliteConnection.ClearAllPools(); + DeleteDirectory(projectRoot); + } + } + [Fact] public void GetJsonIndexHeartbeatPath_UsesWorkerPhaseWhenMainThreadIsIdle() {