gitnexus 1.6.12-rc.24 → 1.6.12-rc.25

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.
@@ -15,6 +15,7 @@ import { createRequire } from 'node:module';
15
15
  import { fileURLToPath } from 'node:url';
16
16
  import { dirname, resolve } from 'node:path';
17
17
  import { Worker } from 'node:worker_threads';
18
+ import { isHeuristicEdgeReason } from '../graph/edge-reasons.js';
18
19
  const __filename = fileURLToPath(import.meta.url);
19
20
  const __dirname = dirname(__filename);
20
21
  // Navigate to package root (works from both src/ and dist/)
@@ -49,6 +50,16 @@ const DEFAULT_COMMUNITY_ENGINE = 'graphology';
49
50
  const LEIDEN_TIMEOUT_MS = 60_000;
50
51
  const ICEBUG_TIMEOUT_MS = 60_000;
51
52
  const MIN_CONFIDENCE_LARGE = 0.5;
53
+ /**
54
+ * Whether a large graph's projection may include this edge.
55
+ *
56
+ * The confidence floor alone lets every global-name-fallback edge through — it
57
+ * is emitted at exactly `MIN_CONFIDENCE_LARGE`, so `confidence <` never
58
+ * excludes it. Clustering on unique-name guesses fuses unrelated areas into one
59
+ * community, which is precisely the noise the large-graph floor exists to
60
+ * remove, so the reason is checked too — see `graph/edge-reasons.ts`.
61
+ */
62
+ const isLargeGraphEligible = (confidence, reason) => confidence >= MIN_CONFIDENCE_LARGE && !isHeuristicEdgeReason(reason);
52
63
  export const resolveCommunityDetectionEngine = (raw = process.env[COMMUNITY_ENGINE_ENV]) => {
53
64
  if (raw === undefined || raw.trim() === '')
54
65
  return DEFAULT_COMMUNITY_ENGINE;
@@ -153,12 +164,18 @@ export const buildCommunityProjection = (knowledgeGraph) => {
153
164
  const isLarge = symbolCount > 10_000;
154
165
  const connectedNodes = new Set();
155
166
  const nodeDegree = new Map();
156
- // Field-wise scan (#2680): this walks every edge and reads only these four,
167
+ // Field-wise scan (#2680): this walks every edge and reads only these five,
157
168
  // so taking objects would allocate one per edge for nothing.
158
- knowledgeGraph.forEachRelationshipFields((sourceId, targetId, type, confidence) => {
169
+ knowledgeGraph.forEachRelationshipFields((sourceId, targetId, type, confidence, reason) => {
159
170
  if (!isClusteringRelationship(type) || sourceId === targetId)
160
171
  return;
161
- if (isLarge && confidence < MIN_CONFIDENCE_LARGE)
172
+ // A name-guessed edge must not join two nodes into a community at ANY graph
173
+ // size — the rationale in `graph/edge-reasons.ts` is about what the edge
174
+ // claims, not about how many symbols surround it. Process tracing applies
175
+ // the same reason gate unconditionally (`process-processor.ts`).
176
+ if (isHeuristicEdgeReason(reason))
177
+ return;
178
+ if (isLarge && !isLargeGraphEligible(confidence, reason))
162
179
  return;
163
180
  connectedNodes.add(sourceId);
164
181
  connectedNodes.add(targetId);
@@ -189,10 +206,12 @@ export const buildCommunityProjection = (knowledgeGraph) => {
189
206
  }
190
207
  const seenEdges = new Set();
191
208
  const edges = [];
192
- knowledgeGraph.forEachRelationshipFields((sourceId, targetId, type, confidence) => {
209
+ knowledgeGraph.forEachRelationshipFields((sourceId, targetId, type, confidence, reason) => {
193
210
  if (!isClusteringRelationship(type) || sourceId === targetId)
194
211
  return;
195
- if (isLarge && confidence < MIN_CONFIDENCE_LARGE)
212
+ if (isHeuristicEdgeReason(reason))
213
+ return;
214
+ if (isLarge && !isLargeGraphEligible(confidence, reason))
196
215
  return;
197
216
  const sourceIndex = nodeIndexById.get(sourceId);
198
217
  const targetIndex = nodeIndexById.get(targetId);
@@ -9,6 +9,7 @@
9
9
  *
10
10
  * Processes help agents understand how features work through the codebase.
11
11
  */
12
+ import { isHeuristicEdgeReason } from '../graph/edge-reasons.js';
12
13
  import { calculateEntryPointScore, isTestFile } from './entry-point-scoring.js';
13
14
  import { SupportedLanguages } from '../../_shared/index.js';
14
15
  import { isDev } from './utils/env.js';
@@ -289,11 +290,21 @@ outwardActionSites = []) => {
289
290
  * traces to jump across unrelated code areas.
290
291
  */
291
292
  const MIN_TRACE_CONFIDENCE = 0.5;
293
+ /**
294
+ * True when an edge may seed or extend a traced flow.
295
+ *
296
+ * The confidence floor alone is not sufficient: the global-name fallback emits
297
+ * at exactly `MIN_TRACE_CONFIDENCE`, so a `<` comparison admits every one of
298
+ * its guesses. A flow assembled from name guesses reads as a real execution
299
+ * path through code that may never call each other, so the reason is checked
300
+ * too — see `graph/edge-reasons.ts`.
301
+ */
302
+ const isTraceableCallsEdge = (type, confidence, reason) => type === 'CALLS' && confidence >= MIN_TRACE_CONFIDENCE && !isHeuristicEdgeReason(reason);
292
303
  const buildCallsGraph = (graph) => {
293
304
  const adj = new Map();
294
- // Field-wise scan (#2680) — whole-graph walk, four fields, no object needed.
295
- graph.forEachRelationshipFields((sourceId, targetId, type, confidence) => {
296
- if (type !== 'CALLS' || confidence < MIN_TRACE_CONFIDENCE)
305
+ // Field-wise scan (#2680) — whole-graph walk, five fields, no object needed.
306
+ graph.forEachRelationshipFields((sourceId, targetId, type, confidence, reason) => {
307
+ if (!isTraceableCallsEdge(type, confidence, reason))
297
308
  return;
298
309
  const existing = adj.get(sourceId);
299
310
  if (existing === undefined)
@@ -305,8 +316,8 @@ const buildCallsGraph = (graph) => {
305
316
  };
306
317
  const buildReverseCallsGraph = (graph) => {
307
318
  const adj = new Map();
308
- graph.forEachRelationshipFields((sourceId, targetId, type, confidence) => {
309
- if (type !== 'CALLS' || confidence < MIN_TRACE_CONFIDENCE)
319
+ graph.forEachRelationshipFields((sourceId, targetId, type, confidence, reason) => {
320
+ if (!isTraceableCallsEdge(type, confidence, reason))
310
321
  return;
311
322
  const existing = adj.get(targetId);
312
323
  if (existing === undefined)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.12-rc.24",
3
+ "version": "1.6.12-rc.25",
4
4
  "description": "Graph-powered code intelligence for AI agents. Index any codebase, query via MCP or CLI.",
5
5
  "author": "Abhigyan Patwari",
6
6
  "license": "PolyForm-Noncommercial-1.0.0",