diff --git a/gitnexus/src/core/run-analyze.ts b/gitnexus/src/core/run-analyze.ts index d18185824..c7c945afb 100644 --- a/gitnexus/src/core/run-analyze.ts +++ b/gitnexus/src/core/run-analyze.ts @@ -669,6 +669,7 @@ export async function runFullAnalysis( // ── Phase 3: FTS (85–90%) ───────────────────────────────────────── progress('fts', 85, 'Creating search indexes...'); + let ftsIndexed = false; try { await createSearchFTSIndexes({ onIndexStart: options.verbose @@ -684,11 +685,17 @@ export async function runFullAnalysis( `⚠️ FTS verification warning - missing indexes: ${missingIndexNames.join(', ')}. ` + 'BM25 keyword search will be degraded. Upgrade macOS or run on a compatible platform to enable FTS.', ); + progress('fts', 90, 'Search indexes degraded (BM25 unavailable)'); } else { + ftsIndexed = true; progress('fts', 90, 'Search indexes ready'); } } catch (ftsErr: unknown) { const ftsMsg = ftsErr instanceof Error ? ftsErr.message : String(ftsErr); + // Only suppress known FTS-extension-unavailable errors; rethrow DB/schema/programming errors. + if (!ftsMsg.includes('FTS extension unavailable')) { + throw ftsErr; + } log(`⚠️ FTS creation skipped (non-fatal): ${ftsMsg}`); progress('fts', 90, 'Search indexes skipped (FTS unavailable)'); } @@ -880,7 +887,7 @@ export async function runFullAnalysis( }, capabilities: { graph: { provider: 'ladybugdb', status: runtimeCapabilities.graph }, - fts: { provider: 'ladybugdb-fts', status: runtimeCapabilities.fts }, + fts: { provider: 'ladybugdb-fts', status: ftsIndexed ? runtimeCapabilities.fts : 'degraded' }, vectorSearch: { provider: effectiveSemanticMode === 'vector-index' ? 'ladybugdb-vector' : 'exact-scan', status: embeddingCount > 0 ? effectiveSemanticMode : 'unavailable', diff --git a/gitnexus/test/unit/run-analyze-fts-repair.test.ts b/gitnexus/test/unit/run-analyze-fts-repair.test.ts index 7b7030ee4..b755a8c2b 100644 --- a/gitnexus/test/unit/run-analyze-fts-repair.test.ts +++ b/gitnexus/test/unit/run-analyze-fts-repair.test.ts @@ -227,17 +227,33 @@ describe('runFullAnalysis FTS repair and verification failure paths', () => { const tmpRepo = await createTempDir('gitnexus-run-analyze-full-verify-fail-'); try { const { runFullAnalysis } = await import('../../src/core/run-analyze.js'); + + const logMessages: string[] = []; + const progressMessages: string[] = []; + // FTS verification failure is now non-fatal — analyze completes with a warning // instead of throwing, so embedding generation can proceed. const result = await runFullAnalysis( tmpRepo.dbPath, { force: true }, { - onProgress: () => {}, + onProgress: (_phase, _pct, msg) => { + if (msg) progressMessages.push(msg); + }, + onLog: (msg: string) => { + logMessages.push(msg); + }, }, ); expect(result).toBeDefined(); expect(result.repoPath).toContain('gitnexus-run-analyze-full-verify-fail-'); + + // Verify warning was logged about missing FTS indexes + expect(logMessages.some((m) => /FTS verification warning/i.test(m))).toBe(true); + + // Verify progress did NOT report "Search indexes ready" (it should report degraded) + expect(progressMessages).not.toContain('Search indexes ready'); + expect(progressMessages.some((m) => /degraded|unavailable/i.test(m))).toBe(true); } finally { await tmpRepo.cleanup(); }