sdl-mcp 0.11.11 → 0.11.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/cli/commands/serve.d.ts.map +1 -1
  3. package/dist/cli/commands/serve.js +233 -217
  4. package/dist/cli/commands/serve.js.map +1 -1
  5. package/dist/db/ladybug.d.ts +6 -0
  6. package/dist/db/ladybug.d.ts.map +1 -1
  7. package/dist/db/ladybug.js +32 -0
  8. package/dist/db/ladybug.js.map +1 -1
  9. package/dist/indexer/indexer-pass1.d.ts.map +1 -1
  10. package/dist/indexer/indexer-pass1.js +3 -1
  11. package/dist/indexer/indexer-pass1.js.map +1 -1
  12. package/dist/indexer/indexer.d.ts +1 -0
  13. package/dist/indexer/indexer.d.ts.map +1 -1
  14. package/dist/indexer/indexer.js +643 -674
  15. package/dist/indexer/indexer.js.map +1 -1
  16. package/dist/indexer/language-packs.js +16 -1
  17. package/dist/indexer/language-packs.js.map +1 -1
  18. package/dist/indexer/provider-first/materializer.d.ts +2 -0
  19. package/dist/indexer/provider-first/materializer.d.ts.map +1 -1
  20. package/dist/indexer/provider-first/materializer.js +82 -0
  21. package/dist/indexer/provider-first/materializer.js.map +1 -1
  22. package/dist/indexer/provider-first/source-call-proof.d.ts.map +1 -1
  23. package/dist/indexer/provider-first/source-call-proof.js +25 -0
  24. package/dist/indexer/provider-first/source-call-proof.js.map +1 -1
  25. package/dist/main.js +62 -52
  26. package/dist/main.js.map +1 -1
  27. package/dist/startup/process-handlers.d.ts.map +1 -1
  28. package/dist/startup/process-handlers.js +13 -3
  29. package/dist/startup/process-handlers.js.map +1 -1
  30. package/dist/util/logger.d.ts.map +1 -1
  31. package/dist/util/logger.js +3 -2
  32. package/dist/util/logger.js.map +1 -1
  33. package/dist/util/shutdown.d.ts +4 -0
  34. package/dist/util/shutdown.d.ts.map +1 -1
  35. package/dist/util/shutdown.js +26 -6
  36. package/dist/util/shutdown.js.map +1 -1
  37. package/dist/util/stdio-safety.d.ts +3 -0
  38. package/dist/util/stdio-safety.d.ts.map +1 -0
  39. package/dist/util/stdio-safety.js +20 -0
  40. package/dist/util/stdio-safety.js.map +1 -0
  41. package/package.json +5 -5
@@ -11,7 +11,7 @@ import { finalizeDerivedState } from "./finalize-derived-state.js";
11
11
  import { watchRepositoryWithIndexer } from "./watcher.js";
12
12
  import { IndexingConfigSchema, } from "../config/types.js";
13
13
  import { loadConfig } from "../config/loadConfig.js";
14
- import { buildDeferredIndexes, closeLadybugDb, flushStaleFinalizers, getLadybugConn, getLadybugDbPath, initLadybugDb, preIndexCheckpoint, withWriteConn, } from "../db/ladybug.js";
14
+ import { buildDeferredIndexes, closeLadybugDb, ensureCriticalSymbolFtsIndex, flushStaleFinalizers, getLadybugConn, getLadybugDbPath, initLadybugDb, preIndexCheckpoint, withWriteConn, } from "../db/ladybug.js";
15
15
  import { withPostIndexWriteSession } from "../db/write-session.js";
16
16
  import * as ladybugDb from "../db/ladybug-queries.js";
17
17
  import { derivedStateIsStale, getDerivedState, markDerivedStateDirty, recordDerivedStateError, } from "../db/ladybug-derived-state.js";
@@ -38,7 +38,7 @@ import { flagStaleMemoriesForChangedFiles, importMemoryFilesFromDisk, } from "./
38
38
  import { withIndexingGate } from "../mcp/indexing-gate.js";
39
39
  import { isInToolDispatch, runToolDispatch, waitForToolDispatchIdle, } from "../mcp/dispatch-limiter.js";
40
40
  import { executeProviderFirstLspIncremental, executeProviderFirstLspFull, executeProviderFirstScipIncremental, executeProviderFirstScipFull, resolveProviderFirstExecutionPlan, } from "./provider-first/executor.js";
41
- import { materializeProviderFacts, providerFirstGraphRowTotal, } from "./provider-first/materializer.js";
41
+ import { filterProviderFactsByExcludedPaths, filterProviderRowsByExcludedPaths, materializeProviderFacts, providerFirstGraphRowTotal, } from "./provider-first/materializer.js";
42
42
  import { collectLegacyFallbackShadowRows, mergeProviderFirstGraphRows, } from "./provider-first/legacy-shadow-rows.js";
43
43
  import { resolveProviderFirstPipeline } from "./provider-first/planner.js";
44
44
  import { persistProviderFirstProvenance } from "./provider-first/provenance.js";
@@ -846,6 +846,17 @@ export function resolveProviderFirstReadinessGates(params) {
846
846
  shadowStagingSkipReason: params.callProofSkipReason ?? params.skippedLegacyFallbackReason,
847
847
  };
848
848
  }
849
+ export function resolveProviderFirstShadowRepoScopeSkipReason(repoId, activeRepoIds) {
850
+ const otherRepoIds = [
851
+ ...new Set(activeRepoIds.filter((activeRepoId) => activeRepoId !== repoId)),
852
+ ].sort();
853
+ if (otherRepoIds.length === 0)
854
+ return undefined;
855
+ const sample = otherRepoIds.slice(0, 3).join(", ");
856
+ const omitted = otherRepoIds.length > 3 ? ` (+${otherRepoIds.length - 3} more)` : "";
857
+ return ("shadow staging skipped because provider-first shadow DBs are " +
858
+ `single-repo scoped and the active DB contains other repo(s): ${sample}${omitted}`);
859
+ }
849
860
  function skippedProviderFirstShadowBuild(params) {
850
861
  return {
851
862
  status: "skipped",
@@ -969,88 +980,6 @@ export function clearProviderGraphRowsForGc(rows) {
969
980
  rows.edges.length = 0;
970
981
  rows.changedFileIds.clear();
971
982
  }
972
- function filterProviderRowsByExcludedPaths(rows, excludedPaths) {
973
- if (excludedPaths.size === 0)
974
- return rows;
975
- const files = rows.files.filter((file) => !excludedPaths.has(file.relPath));
976
- const keptFileIds = new Set(files.map((file) => file.fileId));
977
- const symbols = rows.symbols.filter((symbol) => keptFileIds.has(symbol.fileId));
978
- const internalSymbolIds = new Set(symbols.map((symbol) => symbol.symbolId));
979
- const externalSymbolIds = new Set(rows.externalSymbols.map((symbol) => symbol.symbolId));
980
- const allowedSymbolIds = new Set([
981
- ...internalSymbolIds,
982
- ...externalSymbolIds,
983
- ]);
984
- const edges = rows.edges.filter((edge) => allowedSymbolIds.has(edge.fromSymbolId) &&
985
- allowedSymbolIds.has(edge.toSymbolId) &&
986
- (internalSymbolIds.has(edge.fromSymbolId) ||
987
- internalSymbolIds.has(edge.toSymbolId)));
988
- const referencedExternalSymbolIds = new Set();
989
- for (const edge of edges) {
990
- if (externalSymbolIds.has(edge.fromSymbolId)) {
991
- referencedExternalSymbolIds.add(edge.fromSymbolId);
992
- }
993
- if (externalSymbolIds.has(edge.toSymbolId)) {
994
- referencedExternalSymbolIds.add(edge.toSymbolId);
995
- }
996
- }
997
- const externalSymbols = rows.externalSymbols.filter((symbol) => referencedExternalSymbolIds.has(symbol.symbolId));
998
- return {
999
- files,
1000
- symbols,
1001
- externalSymbols,
1002
- edges,
1003
- changedFileIds: new Set(files.map((file) => file.fileId)),
1004
- };
1005
- }
1006
- function filterProviderFactsByExcludedPaths(facts, excludedPaths) {
1007
- const filePathAllowed = (relPath) => !excludedPaths.has(normalizePath(relPath));
1008
- const files = facts.files.filter((fact) => filePathAllowed(fact.relPath));
1009
- const symbols = facts.symbols.filter((fact) => filePathAllowed(fact.relPath));
1010
- const internalSymbolIds = new Set(symbols.map((symbol) => symbol.symbolId));
1011
- const externalSymbolIds = new Set(facts.externalSymbols.map((symbol) => symbol.symbolId));
1012
- const allowedSymbolIds = new Set([
1013
- ...internalSymbolIds,
1014
- ...externalSymbolIds,
1015
- ]);
1016
- const edges = facts.edges.filter((edge) => allowedSymbolIds.has(edge.sourceSymbolId) &&
1017
- allowedSymbolIds.has(edge.targetSymbolId) &&
1018
- (internalSymbolIds.has(edge.sourceSymbolId) ||
1019
- internalSymbolIds.has(edge.targetSymbolId)));
1020
- const referencedExternalSymbolIds = new Set();
1021
- for (const edge of edges) {
1022
- if (externalSymbolIds.has(edge.sourceSymbolId)) {
1023
- referencedExternalSymbolIds.add(edge.sourceSymbolId);
1024
- }
1025
- if (externalSymbolIds.has(edge.targetSymbolId)) {
1026
- referencedExternalSymbolIds.add(edge.targetSymbolId);
1027
- }
1028
- }
1029
- const externalSymbols = facts.externalSymbols.filter((symbol) => referencedExternalSymbolIds.has(symbol.symbolId));
1030
- const diagnostics = facts.diagnostics.filter((fact) => filePathAllowed(fact.relPath));
1031
- const coverage = facts.coverage.filter((fact) => filePathAllowed(fact.relPath));
1032
- const occurrences = facts.occurrences.filter((fact) => filePathAllowed(fact.relPath));
1033
- const sourceLinesByPath = facts.sourceLinesByPath &&
1034
- new Map([...facts.sourceLinesByPath.entries()].filter(([relPath]) => filePathAllowed(relPath)));
1035
- const providerRuns = facts.providerRuns.map((run) => ({
1036
- ...run,
1037
- fileCount: files.length,
1038
- symbolCount: symbols.length + externalSymbols.length,
1039
- edgeCount: edges.length,
1040
- diagnosticCount: diagnostics.length,
1041
- }));
1042
- return {
1043
- files,
1044
- symbols,
1045
- occurrences,
1046
- edges,
1047
- externalSymbols,
1048
- diagnostics,
1049
- coverage,
1050
- providerRuns,
1051
- ...(sourceLinesByPath ? { sourceLinesByPath } : {}),
1052
- };
1053
- }
1054
983
  function providerRowsHaveMaterialization(rows) {
1055
984
  return (rows.files.length > 0 ||
1056
985
  rows.symbols.length > 0 ||
@@ -1458,6 +1387,10 @@ async function indexRepoImpl(repoId, mode, onProgress, signal, options, scipPreR
1458
1387
  let providerFirstGenerationId;
1459
1388
  const providerFirstFallbackPaths = new Set();
1460
1389
  const providerFirstChangedFileIds = new Set();
1390
+ const providerFirstShadowRepoScopeSkipReason = providerFirst.selectedPipeline === "providerFirst" &&
1391
+ providerFirstExecutionPlan.canExecute
1392
+ ? resolveProviderFirstShadowRepoScopeSkipReason(repoId, (await ladybugDb.listRepos(conn)).map((repo) => repo.repoId))
1393
+ : undefined;
1461
1394
  let providerFirstScan;
1462
1395
  if (providerFirst.selectedPipeline === "providerFirst") {
1463
1396
  if (providerFirstExecutionPlan.canExecute) {
@@ -1528,6 +1461,11 @@ async function indexRepoImpl(repoId, mode, onProgress, signal, options, scipPreR
1528
1461
  onProgress,
1529
1462
  }));
1530
1463
  recordFinalizeIndexingSubphaseTimings(finalizeResult.timings);
1464
+ await measurePhase("ensureCriticalSymbolFts", async () => {
1465
+ await ensureCriticalSymbolFtsIndex({
1466
+ recordTiming: recordIndexSubphaseTiming,
1467
+ });
1468
+ });
1531
1469
  const freshConn = await getLadybugConn();
1532
1470
  const derivedResult = params.skipDerivedStateReason
1533
1471
  ? await measurePhase("skipDerivedState", async () => {
@@ -1722,665 +1660,691 @@ async function indexRepoImpl(repoId, mode, onProgress, signal, options, scipPreR
1722
1660
  providerFirstExecutor &&
1723
1661
  providerFirstIncrementalBootstrapped) {
1724
1662
  providerFirstTimingStartedAt = Date.now();
1725
- emitProviderFirstProgress(onProgress, "coverageScan", {
1726
- message: "scanning repository for provider-first coverage",
1727
- });
1728
- const providerCoverageScan = await measureProviderFirstPhase("coverageScan", "providerFirstCoverageScan", () => scanRepoForIndex({
1729
- repoId,
1730
- repoRoot: repoRow.rootPath,
1731
- config,
1732
- onProgress,
1733
- deleteRemovedFiles: false,
1734
- }));
1735
- emitProviderFirstProgress(onProgress, "coverageScan", {
1736
- stageCurrent: providerCoverageScan.files.length,
1737
- stageTotal: providerCoverageScan.files.length,
1738
- message: `scanned ${providerCoverageScan.files.length} file(s) for provider-first coverage`,
1739
- });
1740
- let providerFirstRemovedFilesDeleted = false;
1741
- if (providerFirstIncrementalActive &&
1742
- providerCoverageScan.removedFileIds.length > 0) {
1743
- await measureProviderFirstPhase("deleteRemovedFiles", "providerFirstIncrementalDeleteRemovedFiles", () => withWriteConn(async (conn) => {
1744
- await ladybugDb.withTransaction(conn, async (txConn) => {
1745
- await ladybugDb.deleteFilesByIds(txConn, providerCoverageScan.removedFileIds);
1746
- });
1747
- }, postIndexSessionTimeoutMs));
1748
- providerFirstRemovedFilesDeleted = true;
1749
- }
1750
- const providerChangedFiles = providerFirstIncrementalActive
1751
- ? providerFirstIncrementalChangedFiles(providerCoverageScan)
1752
- : providerCoverageScan.files;
1753
- const providerExecutionScan = providerFirstIncrementalActive
1754
- ? filterProviderFirstIncrementalScan(providerCoverageScan, providerChangedFiles)
1755
- : providerCoverageScan;
1756
- if (providerFirstIncrementalActive && providerChangedFiles.length === 0) {
1757
- providerFirstScan = providerCoverageScan;
1663
+ try {
1758
1664
  emitProviderFirstProgress(onProgress, "coverageScan", {
1759
- stageCurrent: 0,
1760
- stageTotal: 0,
1761
- message: "no changed files for provider-first incremental execution",
1665
+ message: "scanning repository for provider-first coverage",
1762
1666
  });
1763
- }
1764
- let providerResult;
1765
- if (!providerFirstIncrementalActive || providerChangedFiles.length > 0) {
1766
- try {
1767
- let providerScipPreRefresh = activeScipPreRefresh;
1768
- let providerIncrementalTempPaths = [];
1667
+ const providerCoverageScan = await measureProviderFirstPhase("coverageScan", "providerFirstCoverageScan", () => scanRepoForIndex({
1668
+ repoId,
1669
+ repoRoot: repoRow.rootPath,
1670
+ config,
1671
+ onProgress,
1672
+ deleteRemovedFiles: false,
1673
+ }));
1674
+ emitProviderFirstProgress(onProgress, "coverageScan", {
1675
+ stageCurrent: providerCoverageScan.files.length,
1676
+ stageTotal: providerCoverageScan.files.length,
1677
+ message: `scanned ${providerCoverageScan.files.length} file(s) for provider-first coverage`,
1678
+ });
1679
+ let providerFirstRemovedFilesDeleted = false;
1680
+ if (providerFirstIncrementalActive &&
1681
+ providerCoverageScan.removedFileIds.length > 0) {
1682
+ await measureProviderFirstPhase("deleteRemovedFiles", "providerFirstIncrementalDeleteRemovedFiles", () => withWriteConn(async (conn) => {
1683
+ await ladybugDb.withTransaction(conn, async (txConn) => {
1684
+ await ladybugDb.deleteFilesByIds(txConn, providerCoverageScan.removedFileIds);
1685
+ });
1686
+ }, postIndexSessionTimeoutMs));
1687
+ providerFirstRemovedFilesDeleted = true;
1688
+ }
1689
+ const providerChangedFiles = providerFirstIncrementalActive
1690
+ ? providerFirstIncrementalChangedFiles(providerCoverageScan)
1691
+ : providerCoverageScan.files;
1692
+ const providerExecutionScan = providerFirstIncrementalActive
1693
+ ? filterProviderFirstIncrementalScan(providerCoverageScan, providerChangedFiles)
1694
+ : providerCoverageScan;
1695
+ if (providerFirstIncrementalActive && providerChangedFiles.length === 0) {
1696
+ providerFirstScan = providerCoverageScan;
1697
+ emitProviderFirstProgress(onProgress, "coverageScan", {
1698
+ stageCurrent: 0,
1699
+ stageTotal: 0,
1700
+ message: "no changed files for provider-first incremental execution",
1701
+ });
1702
+ }
1703
+ let providerResult;
1704
+ if (!providerFirstIncrementalActive || providerChangedFiles.length > 0) {
1769
1705
  try {
1770
- if (providerFirstExecutor === "scipIncremental") {
1771
- const scopedScip = await measureProviderFirstPhase("scipIncrementalGenerate", "providerFirstScipIncrementalGenerate", () => runProviderFirstIncrementalScipIo({
1772
- repoId,
1773
- repoRoot: repoRow.rootPath,
1774
- repoConfig: config,
1775
- appConfig,
1776
- changedFiles: providerChangedFiles,
1777
- signal,
1778
- }));
1779
- providerScipPreRefresh = scopedScip.diagnostics;
1780
- activeScipPreRefresh = scopedScip.diagnostics;
1781
- providerIncrementalTempPaths = scopedScip.tempPaths;
1782
- }
1783
- providerResult = await measureProviderFirstPhase("providerCollection", providerFirstLspExecutor(providerFirstExecutor)
1784
- ? providerFirstIncrementalActive
1785
- ? "providerFirstLspIncremental"
1786
- : "providerFirstLspFull"
1787
- : providerFirstIncrementalActive
1788
- ? "providerFirstScipIncremental"
1789
- : "providerFirstScipFull", () => {
1790
- if (providerFirstExecutor === "lspFull") {
1791
- return executeProviderFirstLspFull({
1706
+ let providerScipPreRefresh = activeScipPreRefresh;
1707
+ let providerIncrementalTempPaths = [];
1708
+ try {
1709
+ if (providerFirstExecutor === "scipIncremental") {
1710
+ const scopedScip = await measureProviderFirstPhase("scipIncrementalGenerate", "providerFirstScipIncrementalGenerate", () => runProviderFirstIncrementalScipIo({
1792
1711
  repoId,
1793
1712
  repoRoot: repoRow.rootPath,
1794
- config: appConfig,
1795
- scannedFiles: providerExecutionScan.files,
1796
- recordPhaseTiming: recordProviderFirstPhaseTiming,
1713
+ repoConfig: config,
1714
+ appConfig,
1715
+ changedFiles: providerChangedFiles,
1797
1716
  signal,
1798
- });
1717
+ }));
1718
+ providerScipPreRefresh = scopedScip.diagnostics;
1719
+ activeScipPreRefresh = scopedScip.diagnostics;
1720
+ providerIncrementalTempPaths = scopedScip.tempPaths;
1799
1721
  }
1800
- if (providerFirstExecutor === "lspIncremental") {
1801
- return executeProviderFirstLspIncremental({
1722
+ providerResult = await measureProviderFirstPhase("providerCollection", providerFirstLspExecutor(providerFirstExecutor)
1723
+ ? providerFirstIncrementalActive
1724
+ ? "providerFirstLspIncremental"
1725
+ : "providerFirstLspFull"
1726
+ : providerFirstIncrementalActive
1727
+ ? "providerFirstScipIncremental"
1728
+ : "providerFirstScipFull", () => {
1729
+ if (providerFirstExecutor === "lspFull") {
1730
+ return executeProviderFirstLspFull({
1731
+ repoId,
1732
+ repoRoot: repoRow.rootPath,
1733
+ config: appConfig,
1734
+ scannedFiles: providerExecutionScan.files,
1735
+ recordPhaseTiming: recordProviderFirstPhaseTiming,
1736
+ signal,
1737
+ });
1738
+ }
1739
+ if (providerFirstExecutor === "lspIncremental") {
1740
+ return executeProviderFirstLspIncremental({
1741
+ repoId,
1742
+ repoRoot: repoRow.rootPath,
1743
+ config: appConfig,
1744
+ scannedFiles: providerExecutionScan.files,
1745
+ recordPhaseTiming: recordProviderFirstPhaseTiming,
1746
+ signal,
1747
+ });
1748
+ }
1749
+ const scipParams = {
1802
1750
  repoId,
1803
1751
  repoRoot: repoRow.rootPath,
1804
1752
  config: appConfig,
1753
+ generatedIndexes: providerScipPreRefresh?.generatedIndexes,
1754
+ generatorFailures: providerScipPreRefresh?.failures,
1755
+ generatorCacheKey: providerScipPreRefresh?.cache?.key,
1756
+ scannedPaths: providerExecutionScan.files.map((file) => file.path),
1805
1757
  scannedFiles: providerExecutionScan.files,
1806
1758
  recordPhaseTiming: recordProviderFirstPhaseTiming,
1759
+ onProgress,
1807
1760
  signal,
1808
- });
1809
- }
1810
- const scipParams = {
1811
- repoId,
1812
- repoRoot: repoRow.rootPath,
1813
- config: appConfig,
1814
- generatedIndexes: providerScipPreRefresh?.generatedIndexes,
1815
- generatorFailures: providerScipPreRefresh?.failures,
1816
- generatorCacheKey: providerScipPreRefresh?.cache?.key,
1817
- scannedPaths: providerExecutionScan.files.map((file) => file.path),
1818
- scannedFiles: providerExecutionScan.files,
1819
- recordPhaseTiming: recordProviderFirstPhaseTiming,
1820
- onProgress,
1821
- signal,
1822
- };
1823
- return providerFirstExecutor === "scipIncremental"
1824
- ? executeProviderFirstScipIncremental(scipParams)
1825
- : executeProviderFirstScipFull(scipParams);
1826
- });
1827
- }
1828
- finally {
1829
- await cleanupProviderFirstIncrementalTempPaths(providerIncrementalTempPaths);
1830
- }
1831
- }
1832
- catch (err) {
1833
- if (err instanceof ProviderFirstGraphValidationError) {
1834
- throw err;
1761
+ };
1762
+ return providerFirstExecutor === "scipIncremental"
1763
+ ? executeProviderFirstScipIncremental(scipParams)
1764
+ : executeProviderFirstScipFull(scipParams);
1765
+ });
1766
+ }
1767
+ finally {
1768
+ await cleanupProviderFirstIncrementalTempPaths(providerIncrementalTempPaths);
1769
+ }
1835
1770
  }
1836
- const reason = err instanceof Error
1837
- ? err.message
1838
- : `provider-first SCIP execution failed: ${String(err)}`;
1839
- if (providerFirst.requestedMode === "auto") {
1840
- const fallbackReason = formatProviderFirstAutoFallbackReason({
1841
- reason,
1842
- providerFirstIncrementalActive,
1843
- });
1844
- logger.warn("indexRepo: provider-first execution failed, using legacy fallback", {
1845
- repoId,
1846
- reason: fallbackReason,
1847
- });
1848
- providerFirstExecutionFallback = providerFirstFallbackSummary([
1849
- fallbackReason,
1850
- ]);
1851
- if (providerFirstIncrementalActive) {
1852
- // Keep provider-first incremental failures scoped; otherwise the
1853
- // legacy safety fallback degenerates into a full repo pass.
1854
- providerFirstScan = providerExecutionScan;
1855
- for (const file of providerExecutionScan.files) {
1856
- providerFirstFallbackPaths.add(file.path);
1771
+ catch (err) {
1772
+ if (err instanceof ProviderFirstGraphValidationError) {
1773
+ throw err;
1774
+ }
1775
+ const reason = err instanceof Error
1776
+ ? err.message
1777
+ : `provider-first SCIP execution failed: ${String(err)}`;
1778
+ if (providerFirst.requestedMode === "auto") {
1779
+ const fallbackReason = formatProviderFirstAutoFallbackReason({
1780
+ reason,
1781
+ providerFirstIncrementalActive,
1782
+ });
1783
+ logger.warn("indexRepo: provider-first execution failed, using legacy fallback", {
1784
+ repoId,
1785
+ reason: fallbackReason,
1786
+ });
1787
+ providerFirstExecutionFallback = providerFirstFallbackSummary([
1788
+ fallbackReason,
1789
+ ]);
1790
+ if (providerFirstIncrementalActive) {
1791
+ // Keep provider-first incremental failures scoped; otherwise the
1792
+ // legacy safety fallback degenerates into a full repo pass.
1793
+ providerFirstScan = providerExecutionScan;
1794
+ for (const file of providerExecutionScan.files) {
1795
+ providerFirstFallbackPaths.add(file.path);
1796
+ }
1797
+ providerFirstLegacyFallbackComplete = true;
1798
+ providerFirstLegacyFallbackFileCount = providerExecutionScan.files.length;
1799
+ providerFirstLegacyFallbackSamplePaths = providerExecutionScan.files
1800
+ .map((file) => file.path)
1801
+ .slice(0, 10);
1802
+ providerFirstLegacyFallbackStartedAt = Date.now();
1857
1803
  }
1858
- providerFirstLegacyFallbackComplete = true;
1859
- providerFirstLegacyFallbackFileCount = providerExecutionScan.files.length;
1860
- providerFirstLegacyFallbackSamplePaths = providerExecutionScan.files
1861
- .map((file) => file.path)
1862
- .slice(0, 10);
1863
- providerFirstLegacyFallbackStartedAt = Date.now();
1864
1804
  }
1865
- }
1866
- else {
1867
- throw err;
1805
+ else {
1806
+ throw err;
1807
+ }
1868
1808
  }
1869
1809
  }
1870
- }
1871
- if (providerResult) {
1872
- const executionFailureReasons = providerFirstFatalFailureReasons({
1873
- failures: "failures" in providerResult ? providerResult.failures : [],
1874
- providerRowsAvailable: providerResult.rows.files.length > 0,
1875
- });
1876
- if (executionFailureReasons.length > 0) {
1877
- if (providerFirst.requestedMode === "auto") {
1878
- logger.warn("indexRepo: provider-first execution had failures, using legacy fallback", {
1879
- repoId,
1880
- reasons: executionFailureReasons,
1881
- });
1882
- providerFirstExecutionFallback = providerFirstFallbackSummary(executionFailureReasons);
1810
+ if (providerResult) {
1811
+ const executionFailureReasons = providerFirstFatalFailureReasons({
1812
+ failures: "failures" in providerResult ? providerResult.failures : [],
1813
+ providerRowsAvailable: providerResult.rows.files.length > 0,
1814
+ });
1815
+ if (executionFailureReasons.length > 0) {
1816
+ if (providerFirst.requestedMode === "auto") {
1817
+ logger.warn("indexRepo: provider-first execution had failures, using legacy fallback", {
1818
+ repoId,
1819
+ reasons: executionFailureReasons,
1820
+ });
1821
+ providerFirstExecutionFallback = providerFirstFallbackSummary(executionFailureReasons);
1822
+ }
1823
+ else {
1824
+ throw new Error(`Provider-first indexing cannot execute for ${repoId}: ${executionFailureReasons.join("; ")}`);
1825
+ }
1883
1826
  }
1884
1827
  else {
1885
- throw new Error(`Provider-first indexing cannot execute for ${repoId}: ${executionFailureReasons.join("; ")}`);
1886
- }
1887
- }
1888
- else {
1889
- const providerScan = providerExecutionScan;
1890
- providerFirstScan = providerScan;
1891
- if (providerFactsShouldDropCoveragePayloads(providerResult.facts)) {
1892
- clearProviderFactPayloadsForCoverageAnalysis(providerResult.facts);
1828
+ const providerScan = providerExecutionScan;
1829
+ providerFirstScan = providerScan;
1830
+ if (providerFactsShouldDropCoveragePayloads(providerResult.facts)) {
1831
+ clearProviderFactPayloadsForCoverageAnalysis(providerResult.facts);
1832
+ emitProviderFirstProgress(onProgress, "coverageAnalyze", {
1833
+ message: "large provider occurrence payloads released",
1834
+ });
1835
+ }
1836
+ const scanScopedProvider = filterProviderFirstDataToScannedScope({
1837
+ rows: providerResult.rows,
1838
+ facts: providerResult.facts,
1839
+ scannedPaths: providerScan.files.map((file) => file.path),
1840
+ });
1893
1841
  emitProviderFirstProgress(onProgress, "coverageAnalyze", {
1894
- message: "large provider occurrence payloads released",
1842
+ message: "provider rows filtered to scan scope",
1895
1843
  });
1896
- }
1897
- const scanScopedProvider = filterProviderFirstDataToScannedScope({
1898
- rows: providerResult.rows,
1899
- facts: providerResult.facts,
1900
- scannedPaths: providerScan.files.map((file) => file.path),
1901
- });
1902
- emitProviderFirstProgress(onProgress, "coverageAnalyze", {
1903
- message: "provider rows filtered to scan scope",
1904
- });
1905
- const scanScopedProviderPaths = scanScopedProvider.rows.files.map((file) => file.relPath);
1906
- const scanHasCppSemanticPaths = providerScan.files.some((file) => isCppSemanticScanPath(file.path));
1907
- const semanticEligiblePaths = scanHasCppSemanticPaths
1908
- ? await resolveProviderFirstSemanticEligiblePaths({
1909
- repoRoot: repoRow.rootPath,
1844
+ const scanScopedProviderPaths = scanScopedProvider.rows.files.map((file) => file.relPath);
1845
+ const scanHasCppSemanticPaths = providerScan.files.some((file) => isCppSemanticScanPath(file.path));
1846
+ const semanticEligiblePaths = scanHasCppSemanticPaths
1847
+ ? await resolveProviderFirstSemanticEligiblePaths({
1848
+ repoRoot: repoRow.rootPath,
1849
+ scannedPaths: providerScan.files.map((file) => file.path),
1850
+ providerPaths: scanScopedProviderPaths,
1851
+ })
1852
+ : undefined;
1853
+ const coverageReport = analyzeProviderFirstCoverage({
1910
1854
  scannedPaths: providerScan.files.map((file) => file.path),
1855
+ semanticEligiblePaths: semanticEligiblePaths && semanticEligiblePaths.size > 0
1856
+ ? semanticEligiblePaths
1857
+ : undefined,
1911
1858
  providerPaths: scanScopedProviderPaths,
1912
- })
1913
- : undefined;
1914
- const coverageReport = analyzeProviderFirstCoverage({
1915
- scannedPaths: providerScan.files.map((file) => file.path),
1916
- semanticEligiblePaths: semanticEligiblePaths && semanticEligiblePaths.size > 0
1917
- ? semanticEligiblePaths
1918
- : undefined,
1919
- providerPaths: scanScopedProviderPaths,
1920
- coverage: scanScopedProvider.facts.coverage,
1921
- symbols: scanScopedProvider.facts.symbols,
1922
- occurrences: scanScopedProvider.facts.occurrences,
1923
- sourceLinesByPath: scanScopedProvider.facts.sourceLinesByPath,
1924
- });
1925
- emitProviderFirstProgress(onProgress, "coverageAnalyze", {
1926
- message: "provider coverage analyzed",
1927
- });
1928
- if (coverageReport.fatalReasons.length > 0) {
1929
- throw new Error(`Provider-first indexing cannot execute for ${repoId}: ${coverageReport.fatalReasons.join("; ")}`);
1930
- }
1931
- else {
1932
- const legacyFallbackPlan = resolveProviderFirstLegacyFallbackPlan({
1933
- fallbackFileCount: coverageReport.fallbackPaths.size,
1934
- semanticEligibleFallbackFileCount: coverageReport.summary.semanticEligibilityGap?.totalFiles,
1935
- maxLegacyFallbackFiles: providerFirstConfig.maxLegacyFallbackFiles,
1936
- maxSemanticEligibleFallbackFiles: providerFirstConfig.maxSemanticEligibleFallbackFiles,
1859
+ coverage: scanScopedProvider.facts.coverage,
1860
+ symbols: scanScopedProvider.facts.symbols,
1861
+ occurrences: scanScopedProvider.facts.occurrences,
1862
+ sourceLinesByPath: scanScopedProvider.facts.sourceLinesByPath,
1937
1863
  });
1938
- const skippedLegacyFallbackReason = skippedProviderFirstLegacyFallbackReason(legacyFallbackPlan);
1939
- const readinessGates = resolveProviderFirstReadinessGates({
1940
- callProofSkipReason: callProofSkipDerivedStateReason(coverageReport.callProofIncompletePaths),
1941
- skippedLegacyFallbackReason,
1864
+ emitProviderFirstProgress(onProgress, "coverageAnalyze", {
1865
+ message: "provider coverage analyzed",
1942
1866
  });
1943
- providerFirstSkipDerivedStateReason =
1944
- readinessGates.skipDerivedStateReason;
1945
- providerFirstShadowStagingSkipReason =
1946
- readinessGates.shadowStagingSkipReason;
1947
- if (scopedSourceFileListActive) {
1948
- providerFirstShadowStagingSkipReason =
1949
- providerFirstShadowStagingSkipReason
1950
- ? `${providerFirstShadowStagingSkipReason}; ${scopedSourceFileListReason}`
1951
- : scopedSourceFileListReason;
1867
+ if (coverageReport.fatalReasons.length > 0) {
1868
+ throw new Error(`Provider-first indexing cannot execute for ${repoId}: ${coverageReport.fatalReasons.join("; ")}`);
1952
1869
  }
1953
- if (providerFirstIncrementalActive) {
1954
- const incrementalShadowSkipReason = "shadow staging skipped because provider-first incremental materializes only changed files";
1870
+ else {
1871
+ const legacyFallbackPlan = resolveProviderFirstLegacyFallbackPlan({
1872
+ fallbackFileCount: coverageReport.fallbackPaths.size,
1873
+ semanticEligibleFallbackFileCount: coverageReport.summary.semanticEligibilityGap?.totalFiles,
1874
+ maxLegacyFallbackFiles: providerFirstConfig.maxLegacyFallbackFiles,
1875
+ maxSemanticEligibleFallbackFiles: providerFirstConfig.maxSemanticEligibleFallbackFiles,
1876
+ });
1877
+ const skippedLegacyFallbackReason = skippedProviderFirstLegacyFallbackReason(legacyFallbackPlan);
1878
+ const readinessGates = resolveProviderFirstReadinessGates({
1879
+ callProofSkipReason: callProofSkipDerivedStateReason(coverageReport.callProofIncompletePaths),
1880
+ skippedLegacyFallbackReason,
1881
+ });
1882
+ providerFirstSkipDerivedStateReason =
1883
+ readinessGates.skipDerivedStateReason;
1955
1884
  providerFirstShadowStagingSkipReason =
1956
- providerFirstShadowStagingSkipReason
1957
- ? `${providerFirstShadowStagingSkipReason}; ${incrementalShadowSkipReason}`
1958
- : incrementalShadowSkipReason;
1959
- }
1960
- const legacyFallbackPaths = selectProviderFirstLegacyFallbackPaths({
1961
- fallbackPaths: coverageReport.fallbackPaths,
1962
- semanticEligiblePaths,
1963
- parsedFiles: legacyFallbackPlan.parsedFiles,
1964
- });
1965
- const providerRowsExcludedByLegacyFallback = legacyFallbackPlan.runLegacyFallback
1966
- ? selectProviderRowsExcludedByLegacyFallback({
1967
- fallbackPaths: legacyFallbackPaths,
1968
- coverage: scanScopedProvider.facts.coverage,
1969
- })
1970
- : new Set();
1971
- const materializedRows = filterProviderRowsForFallback(scanScopedProvider.rows, providerRowsExcludedByLegacyFallback);
1972
- await measureProviderFirstPhase("persistProvenance", "providerFirstPersistProvenance", () => withWriteConn((conn) => persistProviderFirstProvenance(conn, scanScopedProvider.facts)));
1973
- // The provider fact set can include every SCIP occurrence and source
1974
- // line. Drop it before legacy fallback and versioning so large repos
1975
- // do not carry decoded provider payloads into post-index finalization.
1976
- clearProviderFactPayloadsForGc(providerResult.facts);
1977
- if (scanScopedProvider.facts !== providerResult.facts) {
1978
- clearProviderFactPayloadsForGc(scanScopedProvider.facts);
1979
- }
1980
- if (providerResult.rows !== materializedRows) {
1981
- clearProviderGraphRowsForGc(providerResult.rows);
1982
- }
1983
- if (scanScopedProvider.rows !== materializedRows &&
1984
- scanScopedProvider.rows !== providerResult.rows) {
1985
- clearProviderGraphRowsForGc(scanScopedProvider.rows);
1986
- }
1987
- await measureProviderFirstPhase("postProviderGc", "providerFirstPostProviderGc", () => flushProviderFirstPayloadFinalizers());
1988
- emitProviderFirstProgress(onProgress, "coverageAnalyze", {
1989
- message: "provider payload finalizers flushed",
1990
- });
1991
- providerFirstProviderRows = materializedRows;
1992
- providerFirstGenerationId = providerResult.generationId;
1993
- applyScannedFileMetadataToProviderRows({
1994
- rows: materializedRows,
1995
- scannedFiles: providerScan.files,
1996
- });
1997
- let shadowBuild;
1998
- if (!legacyFallbackPlan.runLegacyFallback) {
1999
- if (providerFirstShadowStagingSkipReason) {
2000
- const shadowStageTotal = providerFirstGraphRowTotal(materializedRows);
2001
- emitProviderFirstProgress(onProgress, "shadowStage", {
2002
- stageCurrent: 0,
2003
- stageTotal: shadowStageTotal,
2004
- message: `shadow staging skipped: ${providerFirstShadowStagingSkipReason}`,
2005
- });
2006
- shadowBuild = skippedProviderFirstShadowBuild({
2007
- generationId: providerResult.generationId,
2008
- activation: providerFirstConfig.activation,
2009
- requestedFormat: providerFirstConfig.stagingFormat,
2010
- rows: materializedRows,
2011
- reason: providerFirstShadowStagingSkipReason,
2012
- });
1885
+ readinessGates.shadowStagingSkipReason;
1886
+ if (scopedSourceFileListActive) {
1887
+ providerFirstShadowStagingSkipReason =
1888
+ providerFirstShadowStagingSkipReason
1889
+ ? `${providerFirstShadowStagingSkipReason}; ${scopedSourceFileListReason}`
1890
+ : scopedSourceFileListReason;
2013
1891
  }
2014
- else {
2015
- const shadowStageTotal = providerFirstGraphRowTotal(materializedRows);
2016
- emitProviderFirstProgress(onProgress, "shadowStage", {
2017
- stageCurrent: 0,
2018
- stageTotal: shadowStageTotal,
2019
- message: "staging provider rows for shadow bulk load",
2020
- });
2021
- shadowBuild = await measureProviderFirstPhase("shadowStage", "providerFirstShadowStage", () => stageProviderFirstShadowBuild({
2022
- repoId,
2023
- generationId: providerResult.generationId,
2024
- activation: providerFirstConfig.activation,
2025
- requestedFormat: providerFirstConfig.stagingFormat,
2026
- activeDbPath: getLadybugDbPath(),
2027
- repoRoot: repoRow.rootPath,
2028
- repoConfigJson: repoRow.configJson,
2029
- rows: materializedRows,
2030
- }));
2031
- shadowBuild.activationResult =
2032
- summarizeProviderFirstShadowActivationReadiness({
2033
- shadowBuild,
2034
- fallbackFiles: 0,
2035
- graphDerivedStateReady: providerFirstSkipDerivedStateReason === undefined,
2036
- shadowContainsFinalizedGraph: false,
2037
- finalizedGraphReasons: providerFirstSkipDerivedStateReason
2038
- ? [providerFirstSkipDerivedStateReason]
2039
- : undefined,
1892
+ if (providerFirstShadowRepoScopeSkipReason) {
1893
+ // ponytail: revisit multi-repo shadow DBs when shadow builds can copy
1894
+ // a complete DB, including non-target repos, before activation.
1895
+ providerFirstShadowStagingSkipReason =
1896
+ providerFirstShadowStagingSkipReason
1897
+ ? `${providerFirstShadowStagingSkipReason}; ${providerFirstShadowRepoScopeSkipReason}`
1898
+ : providerFirstShadowRepoScopeSkipReason;
1899
+ }
1900
+ if (providerFirstIncrementalActive) {
1901
+ const incrementalShadowSkipReason = "shadow staging skipped because provider-first incremental materializes only changed files";
1902
+ providerFirstShadowStagingSkipReason =
1903
+ providerFirstShadowStagingSkipReason
1904
+ ? `${providerFirstShadowStagingSkipReason}; ${incrementalShadowSkipReason}`
1905
+ : incrementalShadowSkipReason;
1906
+ }
1907
+ const legacyFallbackPaths = selectProviderFirstLegacyFallbackPaths({
1908
+ fallbackPaths: coverageReport.fallbackPaths,
1909
+ semanticEligiblePaths,
1910
+ parsedFiles: legacyFallbackPlan.parsedFiles,
1911
+ });
1912
+ const providerRowsExcludedByLegacyFallback = legacyFallbackPlan.runLegacyFallback
1913
+ ? selectProviderRowsExcludedByLegacyFallback({
1914
+ fallbackPaths: legacyFallbackPaths,
1915
+ coverage: scanScopedProvider.facts.coverage,
1916
+ })
1917
+ : new Set();
1918
+ const materializedRows = filterProviderRowsForFallback(scanScopedProvider.rows, providerRowsExcludedByLegacyFallback);
1919
+ await measureProviderFirstPhase("persistProvenance", "providerFirstPersistProvenance", () => withWriteConn((conn) => persistProviderFirstProvenance(conn, scanScopedProvider.facts)));
1920
+ // The provider fact set can include every SCIP occurrence and source
1921
+ // line. Drop it before legacy fallback and versioning so large repos
1922
+ // do not carry decoded provider payloads into post-index finalization.
1923
+ clearProviderFactPayloadsForGc(providerResult.facts);
1924
+ if (scanScopedProvider.facts !== providerResult.facts) {
1925
+ clearProviderFactPayloadsForGc(scanScopedProvider.facts);
1926
+ }
1927
+ if (providerResult.rows !== materializedRows) {
1928
+ clearProviderGraphRowsForGc(providerResult.rows);
1929
+ }
1930
+ if (scanScopedProvider.rows !== materializedRows &&
1931
+ scanScopedProvider.rows !== providerResult.rows) {
1932
+ clearProviderGraphRowsForGc(scanScopedProvider.rows);
1933
+ }
1934
+ await measureProviderFirstPhase("postProviderGc", "providerFirstPostProviderGc", () => flushProviderFirstPayloadFinalizers());
1935
+ emitProviderFirstProgress(onProgress, "coverageAnalyze", {
1936
+ message: "provider payload finalizers flushed",
1937
+ });
1938
+ providerFirstProviderRows = materializedRows;
1939
+ providerFirstGenerationId = providerResult.generationId;
1940
+ applyScannedFileMetadataToProviderRows({
1941
+ rows: materializedRows,
1942
+ scannedFiles: providerScan.files,
1943
+ });
1944
+ let shadowBuild;
1945
+ if (!legacyFallbackPlan.runLegacyFallback) {
1946
+ if (providerFirstShadowStagingSkipReason) {
1947
+ const shadowStageTotal = providerFirstGraphRowTotal(materializedRows);
1948
+ emitProviderFirstProgress(onProgress, "shadowStage", {
1949
+ stageCurrent: 0,
1950
+ stageTotal: shadowStageTotal,
1951
+ message: `shadow staging skipped: ${providerFirstShadowStagingSkipReason}`,
1952
+ });
1953
+ shadowBuild = skippedProviderFirstShadowBuild({
1954
+ generationId: providerResult.generationId,
1955
+ activation: providerFirstConfig.activation,
1956
+ requestedFormat: providerFirstConfig.stagingFormat,
1957
+ rows: materializedRows,
1958
+ reason: providerFirstShadowStagingSkipReason,
2040
1959
  });
2041
- if (shadowBuild.status === "staged") {
2042
- const stagedTotal = providerFirstShadowStageTotal(shadowBuild.counts);
1960
+ }
1961
+ else {
1962
+ const shadowStageTotal = providerFirstGraphRowTotal(materializedRows);
2043
1963
  emitProviderFirstProgress(onProgress, "shadowStage", {
2044
- stageCurrent: stagedTotal,
2045
- stageTotal: stagedTotal,
2046
- message: "provider rows staged for shadow bulk load",
1964
+ stageCurrent: 0,
1965
+ stageTotal: shadowStageTotal,
1966
+ message: "staging provider rows for shadow bulk load",
2047
1967
  });
1968
+ shadowBuild = await measureProviderFirstPhase("shadowStage", "providerFirstShadowStage", () => stageProviderFirstShadowBuild({
1969
+ repoId,
1970
+ generationId: providerResult.generationId,
1971
+ activation: providerFirstConfig.activation,
1972
+ requestedFormat: providerFirstConfig.stagingFormat,
1973
+ activeDbPath: getLadybugDbPath(),
1974
+ repoRoot: repoRow.rootPath,
1975
+ repoConfigJson: repoRow.configJson,
1976
+ rows: materializedRows,
1977
+ }));
1978
+ shadowBuild.activationResult =
1979
+ summarizeProviderFirstShadowActivationReadiness({
1980
+ shadowBuild,
1981
+ fallbackFiles: 0,
1982
+ graphDerivedStateReady: providerFirstSkipDerivedStateReason === undefined,
1983
+ shadowContainsFinalizedGraph: false,
1984
+ finalizedGraphReasons: providerFirstSkipDerivedStateReason
1985
+ ? [providerFirstSkipDerivedStateReason]
1986
+ : undefined,
1987
+ });
1988
+ if (shadowBuild.status === "staged") {
1989
+ const stagedTotal = providerFirstShadowStageTotal(shadowBuild.counts);
1990
+ emitProviderFirstProgress(onProgress, "shadowStage", {
1991
+ stageCurrent: stagedTotal,
1992
+ stageTotal: stagedTotal,
1993
+ message: "provider rows staged for shadow bulk load",
1994
+ });
1995
+ }
2048
1996
  }
2049
1997
  }
2050
- }
2051
- const activeProviderInputHash = scopedSourceFileListActive || providerFirstIncrementalActive
2052
- ? null
2053
- : "generatedIndexes" in providerResult
2054
- ? providerFirstActiveInputFingerprint(providerResult.generatedIndexes)
1998
+ const activeProviderInputHash = scopedSourceFileListActive || providerFirstIncrementalActive
1999
+ ? null
2000
+ : "generatedIndexes" in providerResult
2001
+ ? providerFirstActiveInputFingerprint(providerResult.generatedIndexes)
2002
+ : null;
2003
+ const activeProviderInputRecord = activeProviderInputHash
2004
+ ? await ladybugDb.getScipIngestionRecord(conn, repoId, PROVIDER_FIRST_ACTIVE_INPUT_RECORD_PATH)
2055
2005
  : null;
2056
- const activeProviderInputRecord = activeProviderInputHash
2057
- ? await ladybugDb.getScipIngestionRecord(conn, repoId, PROVIDER_FIRST_ACTIVE_INPUT_RECORD_PATH)
2058
- : null;
2059
- const activeProviderInputMatches = Boolean(activeProviderInputHash &&
2060
- activeProviderInputRecord?.contentHash ===
2061
- activeProviderInputHash &&
2062
- activeProviderInputRecord.truncated !== true);
2063
- const existingProviderSymbolCount = await countExistingScipProviderSymbols(conn, repoId);
2064
- const activeMaterializationPlan = providerFirstIncrementalActive
2065
- ? {
2066
- deleteExistingFileSymbols: true,
2067
- useKnownFreshWriters: true,
2068
- writeEdges: true,
2069
- reuseExistingProviderRows: false,
2070
- }
2071
- : resolveProviderFirstActiveMaterializationPlan({
2072
- existingProviderFileCount: countExistingProviderPrimaryFiles({
2073
- providerFiles: materializedRows.files,
2074
- existingByPath: providerScan.existingByPath,
2075
- }),
2076
- providerSymbolCount: materializedRows.symbols.length,
2077
- activeProviderInputMatches,
2078
- existingProviderSymbolCount,
2079
- });
2080
- if (activeMaterializationPlan.reuseExistingProviderRows) {
2081
- const deleteTotal = providerFirstMaterializePhaseTotal("deleteFileSymbols", materializedRows, activeMaterializationPlan);
2082
- emitProviderFirstProgress(onProgress, "materialize.deleteFileSymbols", {
2083
- stageCurrent: 0,
2084
- stageTotal: deleteTotal,
2085
- message: "provider active stale cleanup skipped for large symbol set",
2086
- });
2087
- const symbolTotal = providerFirstMaterializePhaseTotal("upsertSymbols", materializedRows, activeMaterializationPlan);
2088
- emitProviderFirstProgress(onProgress, "materialize.upsertSymbols", {
2089
- stageCurrent: symbolTotal,
2090
- stageTotal: symbolTotal,
2091
- message: "provider active rows reused for existing large symbol set",
2092
- });
2093
- }
2094
- await measureProviderFirstPhase("materialize", "providerFirstMaterialize", () => {
2006
+ const activeProviderInputMatches = Boolean(activeProviderInputHash &&
2007
+ activeProviderInputRecord?.contentHash ===
2008
+ activeProviderInputHash &&
2009
+ activeProviderInputRecord.truncated !== true);
2010
+ const existingProviderSymbolCount = await countExistingScipProviderSymbols(conn, repoId);
2011
+ const activeMaterializationPlan = providerFirstIncrementalActive
2012
+ ? {
2013
+ deleteExistingFileSymbols: true,
2014
+ useKnownFreshWriters: true,
2015
+ writeEdges: true,
2016
+ reuseExistingProviderRows: false,
2017
+ }
2018
+ : resolveProviderFirstActiveMaterializationPlan({
2019
+ existingProviderFileCount: countExistingProviderPrimaryFiles({
2020
+ providerFiles: materializedRows.files,
2021
+ existingByPath: providerScan.existingByPath,
2022
+ }),
2023
+ providerSymbolCount: materializedRows.symbols.length,
2024
+ activeProviderInputMatches,
2025
+ existingProviderSymbolCount,
2026
+ });
2095
2027
  if (activeMaterializationPlan.reuseExistingProviderRows) {
2096
- return Promise.resolve();
2028
+ const deleteTotal = providerFirstMaterializePhaseTotal("deleteFileSymbols", materializedRows, activeMaterializationPlan);
2029
+ emitProviderFirstProgress(onProgress, "materialize.deleteFileSymbols", {
2030
+ stageCurrent: 0,
2031
+ stageTotal: deleteTotal,
2032
+ message: "provider active stale cleanup skipped for large symbol set",
2033
+ });
2034
+ const symbolTotal = providerFirstMaterializePhaseTotal("upsertSymbols", materializedRows, activeMaterializationPlan);
2035
+ emitProviderFirstProgress(onProgress, "materialize.upsertSymbols", {
2036
+ stageCurrent: symbolTotal,
2037
+ stageTotal: symbolTotal,
2038
+ message: "provider active rows reused for existing large symbol set",
2039
+ });
2097
2040
  }
2098
- return withWriteConn(async (conn) => {
2099
- if (providerRowsHaveMaterialization(materializedRows)) {
2100
- // materializeProviderFacts owns its transaction. Wrapping it
2101
- // in another transaction makes large provider-first writes
2102
- // crash in LadybugDB's native transaction handling.
2103
- await materializeProviderFacts(conn, materializedRows, {
2104
- replaceFileSymbols: true,
2105
- deleteExistingFileSymbols: activeMaterializationPlan.deleteExistingFileSymbols,
2106
- useKnownFreshWriters: activeMaterializationPlan.useKnownFreshWriters,
2107
- writeEdges: activeMaterializationPlan.writeEdges,
2108
- pruneExternalSymbols: !scopedSourceFileListActive &&
2109
- !providerFirstIncrementalActive,
2110
- symbolFtsIndexName: appConfig.semantic?.retrieval?.fts?.indexName,
2111
- measurePhase: async (phaseName, fn) => {
2112
- const substage = `materialize.${phaseName}`;
2113
- const phaseTotal = providerFirstMaterializePhaseTotal(phaseName, materializedRows, activeMaterializationPlan);
2114
- emitProviderFirstProgress(onProgress, substage, {
2115
- stageCurrent: 0,
2116
- stageTotal: phaseTotal,
2117
- message: `provider materialize ${phaseName} start`,
2118
- });
2119
- const startedAt = Date.now();
2120
- try {
2121
- return await fn();
2122
- }
2123
- finally {
2124
- recordProviderFirstPhaseTiming(`materialize.${phaseName}`, Date.now() - startedAt);
2041
+ await measureProviderFirstPhase("materialize", "providerFirstMaterialize", () => {
2042
+ if (activeMaterializationPlan.reuseExistingProviderRows) {
2043
+ return Promise.resolve();
2044
+ }
2045
+ return withWriteConn(async (conn) => {
2046
+ if (providerRowsHaveMaterialization(materializedRows)) {
2047
+ // materializeProviderFacts owns its transaction. Wrapping it
2048
+ // in another transaction makes large provider-first writes
2049
+ // crash in LadybugDB's native transaction handling.
2050
+ await materializeProviderFacts(conn, materializedRows, {
2051
+ replaceFileSymbols: true,
2052
+ deleteExistingFileSymbols: activeMaterializationPlan.deleteExistingFileSymbols,
2053
+ useKnownFreshWriters: activeMaterializationPlan.useKnownFreshWriters,
2054
+ writeEdges: activeMaterializationPlan.writeEdges,
2055
+ pruneExternalSymbols: !scopedSourceFileListActive &&
2056
+ !providerFirstIncrementalActive,
2057
+ symbolFtsIndexName: appConfig.semantic?.retrieval?.fts?.indexName,
2058
+ measurePhase: async (phaseName, fn) => {
2059
+ const substage = `materialize.${phaseName}`;
2060
+ const phaseTotal = providerFirstMaterializePhaseTotal(phaseName, materializedRows, activeMaterializationPlan);
2125
2061
  emitProviderFirstProgress(onProgress, substage, {
2126
- stageCurrent: phaseTotal,
2062
+ stageCurrent: 0,
2127
2063
  stageTotal: phaseTotal,
2128
- message: `provider materialize ${phaseName} done`,
2064
+ message: `provider materialize ${phaseName} start`,
2129
2065
  });
2130
- }
2131
- },
2132
- });
2133
- }
2134
- else if (!scopedSourceFileListActive &&
2135
- !providerFirstIncrementalActive) {
2136
- await ladybugDb.withTransaction(conn, async (txConn) => {
2137
- await ladybugDb.pruneStaleScipExternalSymbols(txConn, repoId, []);
2138
- });
2139
- }
2140
- if (!providerFirstRemovedFilesDeleted &&
2141
- providerScan.removedFileIds.length > 0) {
2142
- await ladybugDb.withTransaction(conn, async (txConn) => {
2143
- await ladybugDb.deleteFilesByIds(txConn, providerScan.removedFileIds);
2144
- });
2145
- }
2146
- if (activeProviderInputHash) {
2147
- await ladybugDb.mergeScipIngestionRecord(conn, {
2148
- id: hashValue({
2066
+ const startedAt = Date.now();
2067
+ try {
2068
+ return await fn();
2069
+ }
2070
+ finally {
2071
+ recordProviderFirstPhaseTiming(`materialize.${phaseName}`, Date.now() - startedAt);
2072
+ emitProviderFirstProgress(onProgress, substage, {
2073
+ stageCurrent: phaseTotal,
2074
+ stageTotal: phaseTotal,
2075
+ message: `provider materialize ${phaseName} done`,
2076
+ });
2077
+ }
2078
+ },
2079
+ });
2080
+ }
2081
+ else if (!scopedSourceFileListActive &&
2082
+ !providerFirstIncrementalActive) {
2083
+ await ladybugDb.withTransaction(conn, async (txConn) => {
2084
+ await ladybugDb.pruneStaleScipExternalSymbols(txConn, repoId, []);
2085
+ });
2086
+ }
2087
+ if (!providerFirstRemovedFilesDeleted &&
2088
+ providerScan.removedFileIds.length > 0) {
2089
+ await ladybugDb.withTransaction(conn, async (txConn) => {
2090
+ await ladybugDb.deleteFilesByIds(txConn, providerScan.removedFileIds);
2091
+ });
2092
+ }
2093
+ if (activeProviderInputHash) {
2094
+ await ladybugDb.mergeScipIngestionRecord(conn, {
2095
+ id: hashValue({
2096
+ repoId,
2097
+ indexPath: PROVIDER_FIRST_ACTIVE_INPUT_RECORD_PATH,
2098
+ }),
2149
2099
  repoId,
2150
2100
  indexPath: PROVIDER_FIRST_ACTIVE_INPUT_RECORD_PATH,
2151
- }),
2152
- repoId,
2153
- indexPath: PROVIDER_FIRST_ACTIVE_INPUT_RECORD_PATH,
2154
- contentHash: activeProviderInputHash,
2155
- ingestedAt: new Date().toISOString(),
2156
- ledgerVersion: providerResult.generationId,
2157
- symbolCount: materializedRows.symbols.length,
2158
- edgeCount: activeMaterializationPlan.writeEdges
2159
- ? materializedRows.edges.length
2160
- : 0,
2161
- externalSymbolCount: materializedRows.externalSymbols.length,
2162
- truncated: !activeMaterializationPlan.writeEdges,
2163
- });
2101
+ contentHash: activeProviderInputHash,
2102
+ ingestedAt: new Date().toISOString(),
2103
+ ledgerVersion: providerResult.generationId,
2104
+ symbolCount: materializedRows.symbols.length,
2105
+ edgeCount: activeMaterializationPlan.writeEdges
2106
+ ? materializedRows.edges.length
2107
+ : 0,
2108
+ externalSymbolCount: materializedRows.externalSymbols.length,
2109
+ truncated: !activeMaterializationPlan.writeEdges,
2110
+ });
2111
+ }
2112
+ }, postIndexSessionTimeoutMs);
2113
+ });
2114
+ invalidateIndexResultCaches(repoId);
2115
+ providerFirstScipMaterialized = true;
2116
+ if (activeMaterializationPlan.reuseExistingProviderRows) {
2117
+ providerFirstMaterializedFiles = 0;
2118
+ providerFirstMaterializedSymbols = 0;
2119
+ providerFirstMaterializedEdges = 0;
2120
+ }
2121
+ else {
2122
+ providerFirstMaterializedFiles = materializedRows.files.length;
2123
+ providerFirstMaterializedSymbols =
2124
+ materializedRows.symbols.length +
2125
+ materializedRows.externalSymbols.length;
2126
+ providerFirstMaterializedEdges = materializedRows.edges.length;
2127
+ for (const fileId of materializedRows.changedFileIds) {
2128
+ providerFirstChangedFileIds.add(fileId);
2164
2129
  }
2165
- }, postIndexSessionTimeoutMs);
2166
- });
2167
- invalidateIndexResultCaches(repoId);
2168
- providerFirstScipMaterialized = true;
2169
- if (activeMaterializationPlan.reuseExistingProviderRows) {
2170
- providerFirstMaterializedFiles = 0;
2171
- providerFirstMaterializedSymbols = 0;
2172
- providerFirstMaterializedEdges = 0;
2173
- }
2174
- else {
2175
- providerFirstMaterializedFiles = materializedRows.files.length;
2176
- providerFirstMaterializedSymbols =
2177
- materializedRows.symbols.length +
2178
- materializedRows.externalSymbols.length;
2179
- providerFirstMaterializedEdges = materializedRows.edges.length;
2180
- for (const fileId of materializedRows.changedFileIds) {
2181
- providerFirstChangedFileIds.add(fileId);
2182
2130
  }
2183
- }
2184
- const executionReasons = [...coverageReport.reasons];
2185
- if (scanScopedProvider.ignoredProviderPaths.length > 0) {
2186
- const sample = scanScopedProvider.ignoredProviderPaths
2187
- .slice(0, 5)
2188
- .join(", ");
2189
- executionReasons.push(`SCIP provider ignored ${scanScopedProvider.ignoredProviderPaths.length} repo-relative file(s) outside the configured scan scope: ${sample}`);
2190
- }
2191
- if (activeMaterializationPlan.reuseExistingProviderRows) {
2192
- executionReasons.push(`active provider rows reused for ${materializedRows.symbols.length} large existing symbol row(s); clean rebuild or shadow activation is required to physically retire stale provider rows`);
2193
- }
2194
- if (scopedSourceFileListActive) {
2195
- executionReasons.push("repo.sourceFileListPath is set; provider-first active row reuse and shadow activation are disabled so this subset run cannot masquerade as a complete graph");
2196
- }
2197
- if (coverageReport.fallbackPaths.size > 0) {
2198
- if (legacyFallbackPlan.runLegacyFallback) {
2199
- executionReasons.push(`legacy fallback indexed ${legacyFallbackPlan.parsedFiles} uncovered or provider-unusable file(s) after provider-first materialization`);
2131
+ const executionReasons = [...coverageReport.reasons];
2132
+ if (scanScopedProvider.ignoredProviderPaths.length > 0) {
2133
+ const sample = scanScopedProvider.ignoredProviderPaths
2134
+ .slice(0, 5)
2135
+ .join(", ");
2136
+ executionReasons.push(`SCIP provider ignored ${scanScopedProvider.ignoredProviderPaths.length} repo-relative file(s) outside the configured scan scope: ${sample}`);
2200
2137
  }
2201
- if (skippedLegacyFallbackReason) {
2202
- executionReasons.push(skippedLegacyFallbackReason);
2138
+ if (activeMaterializationPlan.reuseExistingProviderRows) {
2139
+ executionReasons.push(`active provider rows reused for ${materializedRows.symbols.length} large existing symbol row(s); clean rebuild or shadow activation is required to physically retire stale provider rows`);
2203
2140
  }
2204
- }
2205
- const coverageSummary = {
2206
- ...coverageReport.summary,
2207
- fallbackFiles: legacyFallbackPlan.parsedFiles,
2208
- };
2209
- if (legacyFallbackPlan.skippedFiles > 0) {
2210
- coverageSummary.legacyFallbackSkippedFiles =
2211
- legacyFallbackPlan.skippedFiles;
2212
- coverageSummary.legacyFallbackFileLimit =
2213
- legacyFallbackPlan.fileLimit;
2214
- if (legacyFallbackPlan.semanticEligibleFallbackFiles !== undefined &&
2215
- legacyFallbackPlan.semanticEligibleFileLimit !== undefined) {
2216
- coverageSummary.semanticEligibleFallbackFiles =
2217
- legacyFallbackPlan.semanticEligibleFallbackFiles;
2218
- coverageSummary.semanticEligibleFallbackFileLimit =
2219
- legacyFallbackPlan.semanticEligibleFileLimit;
2141
+ if (scopedSourceFileListActive) {
2142
+ executionReasons.push("repo.sourceFileListPath is set; provider-first active row reuse and shadow activation are disabled so this subset run cannot masquerade as a complete graph");
2220
2143
  }
2221
- }
2222
- if (scanScopedProvider.ignoredProviderPaths.length > 0) {
2223
- coverageSummary.ignoredProviderFiles =
2224
- scanScopedProvider.ignoredProviderPaths.length;
2225
- coverageSummary.ignoredProviderFileSamples =
2226
- scanScopedProvider.ignoredProviderPaths.slice(0, 5);
2227
- }
2228
- providerFirstExecutedSummary = {
2229
- ...providerResult.summary,
2230
- filesProcessed: materializedRows.files.length,
2231
- symbolsIndexed: materializedRows.symbols.length +
2232
- materializedRows.externalSymbols.length,
2233
- edgesCreated: materializedRows.edges.length,
2234
- externalSymbolsIndexed: materializedRows.externalSymbols.length,
2235
- shadowBuild,
2236
- coverage: coverageSummary,
2237
- reasons: executionReasons,
2238
- };
2239
- if (legacyFallbackPlan.runLegacyFallback) {
2240
- logger.info("indexRepo: provider-first materialized; using legacy fallback for uncovered files", {
2241
- repoId,
2144
+ if (coverageReport.fallbackPaths.size > 0) {
2145
+ if (legacyFallbackPlan.runLegacyFallback) {
2146
+ executionReasons.push(`legacy fallback indexed ${legacyFallbackPlan.parsedFiles} uncovered or provider-unusable file(s) after provider-first materialization`);
2147
+ }
2148
+ if (skippedLegacyFallbackReason) {
2149
+ executionReasons.push(skippedLegacyFallbackReason);
2150
+ }
2151
+ }
2152
+ const coverageSummary = {
2153
+ ...coverageReport.summary,
2242
2154
  fallbackFiles: legacyFallbackPlan.parsedFiles,
2243
- reasons: executionReasons,
2244
- });
2245
- providerFirstScan = filterProviderFirstFallbackScan(providerScan, legacyFallbackPaths);
2246
- for (const file of providerFirstScan.files) {
2247
- providerFirstFallbackPaths.add(file.path);
2155
+ };
2156
+ if (legacyFallbackPlan.skippedFiles > 0) {
2157
+ coverageSummary.legacyFallbackSkippedFiles =
2158
+ legacyFallbackPlan.skippedFiles;
2159
+ coverageSummary.legacyFallbackFileLimit =
2160
+ legacyFallbackPlan.fileLimit;
2161
+ if (legacyFallbackPlan.semanticEligibleFallbackFiles !== undefined &&
2162
+ legacyFallbackPlan.semanticEligibleFileLimit !== undefined) {
2163
+ coverageSummary.semanticEligibleFallbackFiles =
2164
+ legacyFallbackPlan.semanticEligibleFallbackFiles;
2165
+ coverageSummary.semanticEligibleFallbackFileLimit =
2166
+ legacyFallbackPlan.semanticEligibleFileLimit;
2167
+ }
2248
2168
  }
2249
- providerFirstLegacyFallbackComplete =
2250
- isProviderFirstLegacyFallbackPlanComplete(legacyFallbackPlan);
2251
- providerFirstLegacyFallbackFileCount =
2252
- legacyFallbackPlan.parsedFiles;
2253
- providerFirstLegacyFallbackSamplePaths = [
2254
- ...legacyFallbackPaths,
2255
- ].slice(0, 10);
2256
- providerFirstLegacyFallbackStartedAt = Date.now();
2257
- }
2258
- else {
2259
- if (activeMaterializationPlan.reuseExistingProviderRows) {
2260
- const versionId = await resolveActiveGraphVersionId("Provider-first SCIP active rows reused");
2261
- let semanticDeferred = await markProviderFirstSemanticReadinessDeferred({
2169
+ if (scanScopedProvider.ignoredProviderPaths.length > 0) {
2170
+ coverageSummary.ignoredProviderFiles =
2171
+ scanScopedProvider.ignoredProviderPaths.length;
2172
+ coverageSummary.ignoredProviderFileSamples =
2173
+ scanScopedProvider.ignoredProviderPaths.slice(0, 5);
2174
+ }
2175
+ providerFirstExecutedSummary = {
2176
+ ...providerResult.summary,
2177
+ filesProcessed: materializedRows.files.length,
2178
+ symbolsIndexed: materializedRows.symbols.length +
2179
+ materializedRows.externalSymbols.length,
2180
+ edgesCreated: materializedRows.edges.length,
2181
+ externalSymbolsIndexed: materializedRows.externalSymbols.length,
2182
+ shadowBuild,
2183
+ coverage: coverageSummary,
2184
+ reasons: executionReasons,
2185
+ };
2186
+ if (legacyFallbackPlan.runLegacyFallback) {
2187
+ logger.info("indexRepo: provider-first materialized; using legacy fallback for uncovered files", {
2262
2188
  repoId,
2263
- versionId,
2264
- appConfig,
2265
- });
2266
- const semanticRefresh = await runProviderFirstDeferredSemanticRefresh({
2267
- versionId,
2268
- semanticDeferred,
2189
+ fallbackFiles: legacyFallbackPlan.parsedFiles,
2190
+ reasons: executionReasons,
2269
2191
  });
2270
- semanticDeferred = semanticRefresh.semanticDeferred;
2271
- providerFirstExecutedSummary = withProviderFirstPhaseTimings(providerFirstExecutedSummary);
2272
- const resultScip = "generatedIndexes" in providerResult
2192
+ providerFirstScan = filterProviderFirstFallbackScan(providerScan, legacyFallbackPaths);
2193
+ for (const file of providerFirstScan.files) {
2194
+ providerFirstFallbackPaths.add(file.path);
2195
+ }
2196
+ providerFirstLegacyFallbackComplete =
2197
+ isProviderFirstLegacyFallbackPlanComplete(legacyFallbackPlan);
2198
+ providerFirstLegacyFallbackFileCount =
2199
+ legacyFallbackPlan.parsedFiles;
2200
+ providerFirstLegacyFallbackSamplePaths = [
2201
+ ...legacyFallbackPaths,
2202
+ ].slice(0, 10);
2203
+ providerFirstLegacyFallbackStartedAt = Date.now();
2204
+ }
2205
+ else {
2206
+ if (activeMaterializationPlan.reuseExistingProviderRows) {
2207
+ const versionId = await resolveActiveGraphVersionId("Provider-first SCIP active rows reused");
2208
+ let semanticDeferred = await markProviderFirstSemanticReadinessDeferred({
2209
+ repoId,
2210
+ versionId,
2211
+ appConfig,
2212
+ });
2213
+ const semanticRefresh = await runProviderFirstDeferredSemanticRefresh({
2214
+ versionId,
2215
+ semanticDeferred,
2216
+ });
2217
+ semanticDeferred = semanticRefresh.semanticDeferred;
2218
+ providerFirstExecutedSummary = withProviderFirstPhaseTimings(providerFirstExecutedSummary);
2219
+ const resultScip = "generatedIndexes" in providerResult
2220
+ ? {
2221
+ generatedIndexes: providerResult.generatedIndexes,
2222
+ failures: providerResult.failures,
2223
+ generatorCache: activeScipPreRefresh?.cache,
2224
+ }
2225
+ : undefined;
2226
+ const result = {
2227
+ versionId,
2228
+ filesProcessed: 0,
2229
+ changedFiles: 0,
2230
+ removedFiles: 0,
2231
+ symbolsIndexed: 0,
2232
+ edgesCreated: 0,
2233
+ clustersComputed: 0,
2234
+ processesTraced: 0,
2235
+ durationMs: Date.now() - startTime,
2236
+ pass1Engine: emptyPass1EngineTelemetry(),
2237
+ scip: resultScip,
2238
+ providerFirst,
2239
+ providerFirstExecution: providerFirstExecutedSummary,
2240
+ semanticDeferred: semanticDeferred || undefined,
2241
+ algorithmRefresh: skippedDerivedStateResult("provider-first active rows reused").algorithmRefresh,
2242
+ };
2243
+ clearProviderGraphRowsForGc(materializedRows);
2244
+ return result;
2245
+ }
2246
+ const versionId = await createOrReuseVersion(providerFirstLspExecutor(providerResult.summary.executor)
2247
+ ? providerFirstIncrementalActive
2248
+ ? "Provider-first LSP incremental index"
2249
+ : "Provider-first LSP index"
2250
+ : providerFirstIncrementalActive
2251
+ ? "Provider-first SCIP incremental index"
2252
+ : "Provider-first SCIP index", true);
2253
+ const pass1Engine = emptyPass1EngineTelemetry();
2254
+ const scip = "generatedIndexes" in providerResult
2273
2255
  ? {
2274
2256
  generatedIndexes: providerResult.generatedIndexes,
2275
2257
  failures: providerResult.failures,
2276
2258
  generatorCache: activeScipPreRefresh?.cache,
2277
2259
  }
2278
2260
  : undefined;
2261
+ const post = await runPostIndexFinalization({
2262
+ versionId,
2263
+ indexMode: providerFirstIncrementalActive ? "incremental" : "full",
2264
+ filesTotal: providerFirstIncrementalActive
2265
+ ? providerCoverageScan.files.length
2266
+ : materializedRows.files.length,
2267
+ filesScanned: materializedRows.files.length,
2268
+ symbolsExtracted: materializedRows.symbols.length +
2269
+ materializedRows.externalSymbols.length,
2270
+ edgesExtracted: materializedRows.edges.length,
2271
+ changedFileIdsForFinalize: scopedSourceFileListActive || providerFirstIncrementalActive
2272
+ ? materializedRows.changedFileIds
2273
+ : undefined,
2274
+ changedTestFilePathsForFinalize: new Set(),
2275
+ changedFileIdsForMemory: materializedRows.changedFileIds,
2276
+ hasIndexMutations: true,
2277
+ callResolutionTelemetry: createCallResolutionTelemetry({
2278
+ repoId,
2279
+ mode: providerFirstIncrementalActive ? "incremental" : "full",
2280
+ pass2EligibleFileCount: 0,
2281
+ registeredResolvers: [],
2282
+ }),
2283
+ pass1Engine,
2284
+ scip,
2285
+ preloadedFileSummarySymbolFactsByFile: buildPreloadedFileSummarySymbolFactsFromRows({
2286
+ files: materializedRows.files,
2287
+ symbols: materializedRows.symbols,
2288
+ }),
2289
+ deferSemanticRefresh: true,
2290
+ skipDerivedStateReason: providerFirstSkipDerivedStateReason,
2291
+ });
2292
+ providerFirstExecutedSummary = {
2293
+ ...providerFirstExecutedSummary,
2294
+ shadowBuild: await finalizeProviderFirstShadowBuild(providerFirstExecutedSummary.shadowBuild, versionId, post.semanticDeferred),
2295
+ };
2296
+ const semanticRefresh = await runProviderFirstDeferredSemanticRefresh({
2297
+ versionId,
2298
+ semanticDeferred: post.semanticDeferred,
2299
+ });
2300
+ providerFirstExecutedSummary = withProviderFirstPhaseTimings(providerFirstExecutedSummary);
2279
2301
  const result = {
2280
2302
  versionId,
2281
- filesProcessed: 0,
2282
- changedFiles: 0,
2283
- removedFiles: 0,
2284
- symbolsIndexed: 0,
2285
- edgesCreated: 0,
2286
- clustersComputed: 0,
2287
- processesTraced: 0,
2303
+ filesProcessed: materializedRows.files.length,
2304
+ changedFiles: materializedRows.changedFileIds.size,
2305
+ removedFiles: providerScan.removedFiles,
2306
+ symbolsIndexed: materializedRows.symbols.length +
2307
+ materializedRows.externalSymbols.length,
2308
+ edgesCreated: materializedRows.edges.length,
2309
+ clustersComputed: post.clustersComputed,
2310
+ processesTraced: post.processesTraced,
2288
2311
  durationMs: Date.now() - startTime,
2289
- pass1Engine: emptyPass1EngineTelemetry(),
2290
- scip: resultScip,
2312
+ summaryStats: semanticRefresh.summaryStats ?? post.summaryStats,
2313
+ timings: phaseTimings
2314
+ ? {
2315
+ totalMs: Date.now() - startTime,
2316
+ phases: phaseTimings,
2317
+ }
2318
+ : undefined,
2319
+ pass1Engine,
2320
+ scip,
2291
2321
  providerFirst,
2292
2322
  providerFirstExecution: providerFirstExecutedSummary,
2293
- semanticDeferred: semanticDeferred || undefined,
2294
- algorithmRefresh: skippedDerivedStateResult("provider-first active rows reused").algorithmRefresh,
2323
+ semanticDeferred: semanticRefresh.semanticDeferred,
2324
+ algorithmRefresh: post.algorithmRefresh,
2295
2325
  };
2296
- clearProviderGraphRowsForGc(materializedRows);
2297
2326
  return result;
2298
2327
  }
2299
- const versionId = await createOrReuseVersion(providerFirstLspExecutor(providerResult.summary.executor)
2300
- ? providerFirstIncrementalActive
2301
- ? "Provider-first LSP incremental index"
2302
- : "Provider-first LSP index"
2303
- : providerFirstIncrementalActive
2304
- ? "Provider-first SCIP incremental index"
2305
- : "Provider-first SCIP index", true);
2306
- const pass1Engine = emptyPass1EngineTelemetry();
2307
- const scip = "generatedIndexes" in providerResult
2308
- ? {
2309
- generatedIndexes: providerResult.generatedIndexes,
2310
- failures: providerResult.failures,
2311
- generatorCache: activeScipPreRefresh?.cache,
2312
- }
2313
- : undefined;
2314
- const post = await runPostIndexFinalization({
2315
- versionId,
2316
- indexMode: providerFirstIncrementalActive ? "incremental" : "full",
2317
- filesTotal: providerFirstIncrementalActive
2318
- ? providerCoverageScan.files.length
2319
- : materializedRows.files.length,
2320
- filesScanned: materializedRows.files.length,
2321
- symbolsExtracted: materializedRows.symbols.length +
2322
- materializedRows.externalSymbols.length,
2323
- edgesExtracted: materializedRows.edges.length,
2324
- changedFileIdsForFinalize: scopedSourceFileListActive || providerFirstIncrementalActive
2325
- ? materializedRows.changedFileIds
2326
- : undefined,
2327
- changedTestFilePathsForFinalize: new Set(),
2328
- changedFileIdsForMemory: materializedRows.changedFileIds,
2329
- hasIndexMutations: true,
2330
- callResolutionTelemetry: createCallResolutionTelemetry({
2331
- repoId,
2332
- mode: providerFirstIncrementalActive ? "incremental" : "full",
2333
- pass2EligibleFileCount: 0,
2334
- registeredResolvers: [],
2335
- }),
2336
- pass1Engine,
2337
- scip,
2338
- preloadedFileSummarySymbolFactsByFile: buildPreloadedFileSummarySymbolFactsFromRows({
2339
- files: materializedRows.files,
2340
- symbols: materializedRows.symbols,
2341
- }),
2342
- deferSemanticRefresh: true,
2343
- skipDerivedStateReason: providerFirstSkipDerivedStateReason,
2344
- });
2345
- providerFirstExecutedSummary = {
2346
- ...providerFirstExecutedSummary,
2347
- shadowBuild: await finalizeProviderFirstShadowBuild(providerFirstExecutedSummary.shadowBuild, versionId, post.semanticDeferred),
2348
- };
2349
- const semanticRefresh = await runProviderFirstDeferredSemanticRefresh({
2350
- versionId,
2351
- semanticDeferred: post.semanticDeferred,
2352
- });
2353
- providerFirstExecutedSummary = withProviderFirstPhaseTimings(providerFirstExecutedSummary);
2354
- const result = {
2355
- versionId,
2356
- filesProcessed: materializedRows.files.length,
2357
- changedFiles: materializedRows.changedFileIds.size,
2358
- removedFiles: providerScan.removedFiles,
2359
- symbolsIndexed: materializedRows.symbols.length +
2360
- materializedRows.externalSymbols.length,
2361
- edgesCreated: materializedRows.edges.length,
2362
- clustersComputed: post.clustersComputed,
2363
- processesTraced: post.processesTraced,
2364
- durationMs: Date.now() - startTime,
2365
- summaryStats: semanticRefresh.summaryStats ?? post.summaryStats,
2366
- timings: phaseTimings
2367
- ? {
2368
- totalMs: Date.now() - startTime,
2369
- phases: phaseTimings,
2370
- }
2371
- : undefined,
2372
- pass1Engine,
2373
- scip,
2374
- providerFirst,
2375
- providerFirstExecution: providerFirstExecutedSummary,
2376
- semanticDeferred: semanticRefresh.semanticDeferred,
2377
- algorithmRefresh: post.algorithmRefresh,
2378
- };
2379
- return result;
2380
2328
  }
2381
2329
  }
2382
2330
  }
2383
2331
  }
2332
+ catch (err) {
2333
+ if (err instanceof ProviderFirstGraphValidationError)
2334
+ throw err;
2335
+ if (providerFirst.requestedMode !== "auto")
2336
+ throw err;
2337
+ const reason = err instanceof Error ? err.message : String(err);
2338
+ const fallbackReason = formatProviderFirstAutoFallbackReason({
2339
+ reason,
2340
+ providerFirstIncrementalActive,
2341
+ });
2342
+ logger.warn("indexRepo: provider-first phase failed (coverage scan or collection), using legacy fallback", { repoId, reason: fallbackReason });
2343
+ providerFirstExecutionFallback = providerFirstFallbackSummary([
2344
+ fallbackReason,
2345
+ ]);
2346
+ // providerFirstScan stays undefined; legacy scanRepoForIndex will run below.
2347
+ }
2384
2348
  }
2385
2349
  const { files, existingByPath, removedFiles, removedFileIds, allFilesUnchanged: scanAllFilesUnchanged, } = providerFirstScan ??
2386
2350
  (await measurePhase("scanRepo", () => scanRepoForIndex({
@@ -2963,6 +2927,11 @@ async function indexRepoImpl(repoId, mode, onProgress, signal, options, scipPreR
2963
2927
  onProgress,
2964
2928
  }));
2965
2929
  recordFinalizeIndexingSubphaseTimings(finalizeResult.timings);
2930
+ await measurePhase("ensureCriticalSymbolFts", async () => {
2931
+ await ensureCriticalSymbolFtsIndex({
2932
+ recordTiming: recordIndexSubphaseTiming,
2933
+ });
2934
+ });
2966
2935
  // Refresh read connection again after version/metrics writes.
2967
2936
  freshConn = await getLadybugConn();
2968
2937
  const derivedResult = providerFirstSkipDerivedStateReason