gitnexus 1.6.6-rc.79 → 1.6.6-rc.80
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.
- package/dist/mcp/local/local-backend.js +100 -5
- package/dist/mcp/tools.js +1 -1
- package/package.json +1 -1
|
@@ -2371,6 +2371,7 @@ export class LocalBackend {
|
|
|
2371
2371
|
*/
|
|
2372
2372
|
async _runImpactBFS(repo, sym, symType, direction, opts) {
|
|
2373
2373
|
const { maxDepth, relationTypes, includeTests, minConfidence } = opts;
|
|
2374
|
+
const skipPerSymbolEnrichment = opts.skipPerSymbolEnrichment ?? false;
|
|
2374
2375
|
const hasExplicitLimit = typeof opts.limit === 'number' && Number.isFinite(opts.limit);
|
|
2375
2376
|
const paginationLimit = hasExplicitLimit
|
|
2376
2377
|
? Math.max(1, Math.min(Math.trunc(opts.limit), 10000))
|
|
@@ -2501,11 +2502,20 @@ export class LocalBackend {
|
|
|
2501
2502
|
const directCount = (grouped[1] || []).length;
|
|
2502
2503
|
let affectedProcesses = [];
|
|
2503
2504
|
let affectedModules = [];
|
|
2505
|
+
// Per-symbol process membership: maps impacted symbol id -> list of processes
|
|
2506
|
+
// it participates in. Populated by a second chunked Cypher pass below when
|
|
2507
|
+
// any process is affected at all. Surfaced as `processes: [...]` on each
|
|
2508
|
+
// byDepth item so consumers can tell which caller belongs to which cron/
|
|
2509
|
+
// webhook/route without a follow-up query.
|
|
2510
|
+
const perSymbolProcesses = new Map();
|
|
2511
|
+
// Chunking bounds for batched DB round-trips. Declared at function scope so
|
|
2512
|
+
// both the in-block enrichment passes and the post-pagination per-symbol
|
|
2513
|
+
// process enrichment can reference them.
|
|
2514
|
+
const CHUNK_SIZE = 100;
|
|
2515
|
+
// Max number of chunks to process to avoid unbounded DB round-trips.
|
|
2516
|
+
// Configurable via env IMPACT_MAX_CHUNKS, default 10 => max items = 1000
|
|
2517
|
+
const MAX_CHUNKS = parseInt(process.env.IMPACT_MAX_CHUNKS || '10', 10);
|
|
2504
2518
|
if (impacted.length > 0) {
|
|
2505
|
-
const CHUNK_SIZE = 100;
|
|
2506
|
-
// Max number of chunks to process to avoid unbounded DB round-trips.
|
|
2507
|
-
// Configurable via env IMPACT_MAX_CHUNKS, default 10 => max items = 1000
|
|
2508
|
-
const MAX_CHUNKS = parseInt(process.env.IMPACT_MAX_CHUNKS || '10', 10);
|
|
2509
2519
|
// ── Process enrichment: batched chunking (bounded by MAX_CHUNKS) ─
|
|
2510
2520
|
// Uses merged Cypher query (WITH + OPTIONAL MATCH) to fetch
|
|
2511
2521
|
// process + entry point info in 1 round-trip per chunk. Converted to
|
|
@@ -2620,6 +2630,9 @@ export class LocalBackend {
|
|
|
2620
2630
|
earliest_broken_step: ep.earliest_broken_step === Infinity ? null : ep.earliest_broken_step,
|
|
2621
2631
|
}))
|
|
2622
2632
|
.sort((a, b) => b.total_hits - a.total_hits);
|
|
2633
|
+
// Per-symbol process membership is populated post-pagination (see below)
|
|
2634
|
+
// so it covers exactly the symbols returned in byDepth, not a pre-capped
|
|
2635
|
+
// flat slice that could miss depth-2+ symbols when depth-1 is large.
|
|
2623
2636
|
// ── Module enrichment: use same cap as process enrichment and parameterized queries
|
|
2624
2637
|
const maxItems = Math.min(impacted.length, MAX_CHUNKS * CHUNK_SIZE);
|
|
2625
2638
|
const cappedImpacted = impacted.slice(0, maxItems);
|
|
@@ -2746,7 +2759,7 @@ export class LocalBackend {
|
|
|
2746
2759
|
if (summaryOnly) {
|
|
2747
2760
|
return base;
|
|
2748
2761
|
}
|
|
2749
|
-
// Apply limit/offset pagination per depth level
|
|
2762
|
+
// Apply limit/offset pagination per depth level.
|
|
2750
2763
|
const paginatedGrouped = {};
|
|
2751
2764
|
let anyTruncated = false;
|
|
2752
2765
|
for (const [depth, items] of Object.entries(grouped)) {
|
|
@@ -2757,8 +2770,81 @@ export class LocalBackend {
|
|
|
2757
2770
|
anyTruncated = true;
|
|
2758
2771
|
}
|
|
2759
2772
|
}
|
|
2773
|
+
// ── Per-symbol process membership enrichment (post-pagination) ───────
|
|
2774
|
+
// Runs after paginatedGrouped is built so we enrich only the IDs that
|
|
2775
|
+
// actually appear in the response. This eliminates the false-empty
|
|
2776
|
+
// processes:[] case where a depth-2+ symbol's flat position in `impacted`
|
|
2777
|
+
// exceeded MAX_CHUNKS*CHUNK_SIZE even though it is returned by byDepth.
|
|
2778
|
+
// Also uses DISTINCT + MIN(r.step) per (symbol, process) pair to avoid
|
|
2779
|
+
// duplicate entries when a symbol has multiple STEP_IN_PROCESS edges.
|
|
2780
|
+
// Skipped entirely when `skipPerSymbolEnrichment` is set (group cross-repo
|
|
2781
|
+
// fan-out, which consumes byDepth but not byDepth[].processes); the
|
|
2782
|
+
// attach-loop below still stamps an empty processes:[] for shape stability.
|
|
2783
|
+
let perSymbolEnrichmentCapped = false;
|
|
2784
|
+
if (affectedProcesses.length > 0 && !skipPerSymbolEnrichment) {
|
|
2785
|
+
// Collect unique IDs from the paginated result in one pass.
|
|
2786
|
+
const pageIds = new Set();
|
|
2787
|
+
for (const items of Object.values(paginatedGrouped)) {
|
|
2788
|
+
for (const it of items) {
|
|
2789
|
+
const id = String(it.id ?? '');
|
|
2790
|
+
if (id)
|
|
2791
|
+
pageIds.add(id);
|
|
2792
|
+
}
|
|
2793
|
+
}
|
|
2794
|
+
// Bound the enrichment to the same ceiling as the aggregation pass
|
|
2795
|
+
// (MAX_CHUNKS * CHUNK_SIZE) so a large paginated page cannot trigger
|
|
2796
|
+
// unbounded DB round-trips (DoD 2.6). When capped, mark the result
|
|
2797
|
+
// partial so callers know some returned symbols may carry an empty
|
|
2798
|
+
// processes:[] that is a cap artifact, not a true absence.
|
|
2799
|
+
const maxPageIds = MAX_CHUNKS * CHUNK_SIZE;
|
|
2800
|
+
let pageIdArr = Array.from(pageIds);
|
|
2801
|
+
if (pageIdArr.length > maxPageIds) {
|
|
2802
|
+
pageIdArr = pageIdArr.slice(0, maxPageIds);
|
|
2803
|
+
perSymbolEnrichmentCapped = true;
|
|
2804
|
+
}
|
|
2805
|
+
for (let i = 0; i < pageIdArr.length; i += CHUNK_SIZE) {
|
|
2806
|
+
const chunkIds = pageIdArr.slice(i, i + CHUNK_SIZE);
|
|
2807
|
+
try {
|
|
2808
|
+
const rows = await executeParameterized(repo.id, `
|
|
2809
|
+
MATCH (s)-[r:CodeRelation {type: 'STEP_IN_PROCESS'}]->(p:Process)
|
|
2810
|
+
WHERE s.id IN $ids
|
|
2811
|
+
RETURN s.id AS sid, p.id AS pid, p.heuristicLabel AS pName,
|
|
2812
|
+
p.processType AS pType, MIN(r.step) AS step
|
|
2813
|
+
`, { ids: chunkIds }).catch(() => []);
|
|
2814
|
+
for (const row of rows) {
|
|
2815
|
+
const sid = row.sid ?? row[0];
|
|
2816
|
+
if (!sid)
|
|
2817
|
+
continue;
|
|
2818
|
+
const procEntry = {
|
|
2819
|
+
id: String(row.pid ?? row[1] ?? ''),
|
|
2820
|
+
label: String(row.pName ?? row[2] ?? ''),
|
|
2821
|
+
processType: String(row.pType ?? row[3] ?? ''),
|
|
2822
|
+
step: Number(row.step ?? row[4] ?? -1),
|
|
2823
|
+
};
|
|
2824
|
+
const list = perSymbolProcesses.get(String(sid));
|
|
2825
|
+
if (list)
|
|
2826
|
+
list.push(procEntry);
|
|
2827
|
+
else
|
|
2828
|
+
perSymbolProcesses.set(String(sid), [procEntry]);
|
|
2829
|
+
}
|
|
2830
|
+
}
|
|
2831
|
+
catch (e) {
|
|
2832
|
+
logQueryError('impact:per-symbol-process-chunk', e);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
// Attach processes field to each paginated item.
|
|
2837
|
+
for (const items of Object.values(paginatedGrouped)) {
|
|
2838
|
+
for (const it of items) {
|
|
2839
|
+
it.processes = perSymbolProcesses.get(String(it.id)) ?? [];
|
|
2840
|
+
}
|
|
2841
|
+
}
|
|
2760
2842
|
return {
|
|
2761
2843
|
...base,
|
|
2844
|
+
// Surface partial if the per-symbol enrichment was capped, even when the
|
|
2845
|
+
// BFS traversal itself completed — some returned symbols may carry an
|
|
2846
|
+
// empty processes:[] that is a cap artifact rather than a true absence.
|
|
2847
|
+
...(perSymbolEnrichmentCapped && { partial: true }),
|
|
2762
2848
|
...(anyTruncated && {
|
|
2763
2849
|
pagination: {
|
|
2764
2850
|
...(Number.isFinite(paginationLimit) && { limit: paginationLimit }),
|
|
@@ -2830,11 +2916,20 @@ export class LocalBackend {
|
|
|
2830
2916
|
'METHOD_IMPLEMENTS',
|
|
2831
2917
|
];
|
|
2832
2918
|
try {
|
|
2919
|
+
// skipPerSymbolEnrichment suppresses ONLY the per-symbol STEP_IN_PROCESS
|
|
2920
|
+
// enrichment pass while preserving byDepth. Group-mode cross-repo fan-out
|
|
2921
|
+
// may fan across many repos; the per-symbol pass adds up to MAX_CHUNKS
|
|
2922
|
+
// extra round-trips per repo, which is unacceptable at group scale. But
|
|
2923
|
+
// cross-impact fan-out DOES consume byDepth (cross-impact.ts reads
|
|
2924
|
+
// fan.byDepth to populate group by_depth), so summaryOnly would wrongly
|
|
2925
|
+
// drop it. Group callers do not consume byDepth[].processes, so skipping
|
|
2926
|
+
// only that enrichment is the correct, targeted suppression.
|
|
2833
2927
|
return await this._runImpactBFS(repo, sym, symType, dir, {
|
|
2834
2928
|
maxDepth: opts.maxDepth,
|
|
2835
2929
|
relationTypes,
|
|
2836
2930
|
includeTests: opts.includeTests,
|
|
2837
2931
|
minConfidence: opts.minConfidence,
|
|
2932
|
+
skipPerSymbolEnrichment: true,
|
|
2838
2933
|
});
|
|
2839
2934
|
}
|
|
2840
2935
|
catch {
|
package/dist/mcp/tools.js
CHANGED
|
@@ -299,7 +299,7 @@ Output includes:
|
|
|
299
299
|
- summary: direct callers, processes affected, modules affected
|
|
300
300
|
- affected_processes: which execution flows break and at which step
|
|
301
301
|
- affected_modules: which functional areas are hit (direct vs indirect)
|
|
302
|
-
- byDepth: affected symbols grouped by traversal depth (paginated by limit/offset; omitted when summaryOnly:true — use byDepthCounts for totals per depth, pagination object when truncated)
|
|
302
|
+
- byDepth: affected symbols grouped by traversal depth (paginated by limit/offset; omitted when summaryOnly:true — use byDepthCounts for totals per depth, pagination object when truncated). Each item includes a processes:[{id,label,processType,step}] field listing the execution flows that symbol participates in. Empty when the symbol has no process membership. Can ALSO be empty when partial:true is set — either the process-aggregation pass hit its cap before detecting affected processes, or per-symbol enrichment was capped on a very large page. When partial:true, do NOT treat processes:[] as proof of no participation; cross-check the top-level affected_processes list.
|
|
303
303
|
|
|
304
304
|
Depth groups:
|
|
305
305
|
- d=1: WILL BREAK (direct callers/importers)
|
package/package.json
CHANGED