Selaa lähdekoodia

fix(cli): include resolution + synthesizer edges in indexAll report (#413)

The orchestrator's per-file counter only sees extraction-phase edges, so the `X nodes, Y edges` line printed after `codegraph init -i` / `codegraph index` undercounts the graph — often by more than half on repos with heavy cross-file resolution (mall: 20 047 reported vs 45 629 actually in the DB).

Snapshot (nodes, edges) before/after the full pipeline in `indexAll` and write the true delta back to the result. New lightweight `QueryBuilder.getNodeAndEdgeCount()` is one round-trip with no per-kind breakdowns. `indexFiles` (no resolution) and `sync` (uses `nodesUpdated`, not `nodesCreated`) are unaffected.

Regression test added: `__tests__/integration/full-pipeline.test.ts > reports edgesCreated including resolution + synthesizer phases`.
Artem Bambalov 3 viikkoa sitten
vanhempi
sitoutus
3808b4d0a8
4 muutettua tiedostoa jossa 60 lisäystä ja 1 poistoa
  1. 9 1
      CHANGELOG.md
  2. 28 0
      __tests__/integration/full-pipeline.test.ts
  3. 13 0
      src/db/queries.ts
  4. 10 0
      src/index.ts

+ 9 - 1
CHANGELOG.md

@@ -9,6 +9,15 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 ## [Unreleased]
 
+### Fixed
+- **`codegraph index` / `init -i` summary now reports the true edge count.**
+  The per-file counter in the orchestrator only saw extraction-phase edges,
+  so resolution and synthesizer edges (often >50% of the graph on
+  cross-file-heavy repos like Spring multi-module Java) were missing from
+  the `X nodes, Y edges` line. Snapshotting the DB before/after the full
+  pipeline now reports the actual additions. Example: indexing
+  `macrozheng/mall` previously reported `20 047 edges` while the DB held
+  `45 629`.
 
 ## [0.9.6] - 2026-05-27
 
@@ -62,7 +71,6 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
   Both targets are tested on the same parameterized contract as the existing five agents (idempotent install, sibling preservation, install/uninstall round-trip), with extra coverage for migration-marker detection, legacy → unified entry migration, sibling `disabled` field preservation, and the cross-target case where Gemini CLI and Antigravity IDE coexist in the same `~/.gemini/`. Closes #399.
 - **Installer target for Kiro (CLI + IDE).** `codegraph install` now detects and configures Kiro out of the box on macOS, Linux, and Windows. Writes `mcpServers.codegraph` to `~/.kiro/settings/mcp.json` (global) or `./.kiro/settings/mcp.json` (local), and the codegraph usage block into a dedicated `~/.kiro/steering/codegraph.md` / `./.kiro/steering/codegraph.md` — Kiro's steering system loads every `*.md` file in `steering/` as agent context, so a dedicated file is the natural surface (no marker-based merging required). Sibling MCP servers in `mcp.json` and unrelated steering files (`product.md`, `tech.md`, etc.) are preserved across install / uninstall. Tested on the same parameterized contract as the other agent targets (idempotent install, sibling preservation, install/uninstall round-trip). Closes #385.
-
 ## [0.9.5] - 2026-05-25
 
 ### Added

+ 28 - 0
__tests__/integration/full-pipeline.test.ts

@@ -241,4 +241,32 @@ describe('Integration: full pipeline', () => {
       cg.destroy();
     }
   }, 30_000);
+
+  it('reports edgesCreated including resolution + synthesizer phases', async () => {
+    // The synthetic project has cross-file imports, calls, and extends —
+    // all wired up in the resolution phase, AFTER the orchestrator's
+    // per-file extraction counter is done. The CLI summary used to read
+    // only the extraction-phase counter and undercount the graph; this
+    // test pins the counter to the true DB totals across all phases.
+    generateSyntheticProject(tempDir, 30);
+
+    const cg = await CodeGraph.init(tempDir, {
+      config: { include: ['**/*.ts'], exclude: [] },
+    });
+
+    try {
+      const result = await cg.indexAll();
+      const stats = cg.getStats();
+
+      expect(result.success).toBe(true);
+      expect(result.nodesCreated).toBe(stats.nodeCount);
+      expect(result.edgesCreated).toBe(stats.edgeCount);
+      // Sanity: cross-file resolution had something to do — calls/extends
+      // edges should exist beyond the bare extraction-time contains edges.
+      const containsOnly = stats.edgesByKind.contains ?? 0;
+      expect(stats.edgeCount).toBeGreaterThan(containsOnly);
+    } finally {
+      cg.destroy();
+    }
+  }, 30_000);
 });

+ 13 - 0
src/db/queries.ts

@@ -1445,6 +1445,19 @@ export class QueryBuilder {
   // Statistics
   // ===========================================================================
 
+  /**
+   * Lightweight (nodes, edges) count snapshot. Used around an index/sync
+   * run to compute true additions across extraction + resolution +
+   * synthesis — the per-phase counter in the orchestrator only sees
+   * extraction's contribution, which is why the CLI summary under-reported
+   * the edge count (resolution + synthesizer edges were invisible).
+   */
+  getNodeAndEdgeCount(): { nodes: number; edges: number } {
+    return this.db
+      .prepare('SELECT (SELECT COUNT(*) FROM nodes) AS nodes, (SELECT COUNT(*) FROM edges) AS edges')
+      .get() as { nodes: number; edges: number };
+  }
+
   /**
    * Get graph statistics
    */

+ 10 - 0
src/index.ts

@@ -325,6 +325,7 @@ export class CodeGraph {
         return { success: false, filesIndexed: 0, filesSkipped: 0, filesErrored: 0, nodesCreated: 0, edgesCreated: 0, errors: [{ message: 'Could not acquire file lock - another process may be indexing', severity: 'error' as const }], durationMs: 0 };
       }
       try {
+        const before = this.queries.getNodeAndEdgeCount();
         const result = await this.orchestrator.indexAll(options.onProgress, options.signal, options.verbose);
 
         // Re-detect frameworks now that the index is populated. The resolver
@@ -367,6 +368,15 @@ export class CodeGraph {
           this.db.runMaintenance();
         }
 
+        // The orchestrator only sees extraction-phase counts; resolution and
+        // synthesizer edges (often >50% of the graph on JVM repos) come later.
+        // Recompute against the DB so the CLI summary reports the true totals.
+        if (result.success && result.filesIndexed > 0) {
+          const after = this.queries.getNodeAndEdgeCount();
+          result.nodesCreated = after.nodes - before.nodes;
+          result.edgesCreated = after.edges - before.edges;
+        }
+
         return result;
       } finally {
         this.fileLock.release();