gitnexus 1.6.11-rc.1 → 1.6.11-rc.2
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/core/index-freshness.d.ts +2 -2
- package/dist/core/index-freshness.js +13 -0
- package/dist/core/ingestion/parsing-processor.d.ts +2 -0
- package/dist/core/ingestion/parsing-processor.js +5 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +3 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +7 -0
- package/dist/core/ingestion/pipeline-phases/parse.d.ts +4 -0
- package/dist/core/ingestion/pipeline.js +4 -1
- package/dist/core/ingestion/scope-extractor-bridge.js +8 -2
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +14 -2
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +2 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +11 -1
- package/dist/core/ingestion/scope-resolution/scope-extraction-failures.d.ts +14 -0
- package/dist/core/ingestion/scope-resolution/scope-extraction-failures.js +35 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +6 -0
- package/dist/core/ingestion/workers/parse-worker.js +7 -1
- package/dist/core/ingestion/workers/result-merge.js +4 -1
- package/dist/core/run-analyze.js +6 -0
- package/dist/mcp/local/local-backend.d.ts +2 -0
- package/dist/mcp/local/local-backend.js +29 -0
- package/dist/mcp/tools.js +6 -4
- package/dist/storage/parse-cache.js +6 -6
- package/dist/storage/repo-meta.d.ts +17 -1
- package/dist/storage/repo-meta.js +1 -1
- package/dist/types/pipeline.d.ts +4 -0
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RepoMeta } from '../storage/repo-manager.js';
|
|
2
|
-
export declare const INDEX_INCOMPLETE_REASONS: readonly ["incremental-in-progress", "embedding-checkpoint-pending", "embedding-count-unverified", "graph-write-collapsed"];
|
|
2
|
+
export declare const INDEX_INCOMPLETE_REASONS: readonly ["incremental-in-progress", "embedding-checkpoint-pending", "embedding-count-unverified", "graph-write-collapsed", "scope-extraction-unverified", "scope-extraction-failed"];
|
|
3
3
|
export type IndexIncompleteReason = (typeof INDEX_INCOMPLETE_REASONS)[number];
|
|
4
4
|
/**
|
|
5
5
|
* Fraction of the pipeline's relationship count that must survive into the DB
|
|
@@ -77,4 +77,4 @@ export declare function detectGraphWriteCollapse(expected: number,
|
|
|
77
77
|
*/
|
|
78
78
|
persisted: number | undefined): GraphWriteCollapseVerdict;
|
|
79
79
|
/** Stable machine-readable reasons an index cannot be certified complete. */
|
|
80
|
-
export declare function getIndexIncompleteReasons(meta: Pick<RepoMeta, 'incrementalInProgress' | 'embeddingCheckpoint' | 'graphWriteCollapsed'> | null | undefined): IndexIncompleteReason[];
|
|
80
|
+
export declare function getIndexIncompleteReasons(meta: Pick<RepoMeta, 'incrementalInProgress' | 'embeddingCheckpoint' | 'graphWriteCollapsed' | 'scopeExtractionFailures' | 'scopeExtractionReceipt'> | null | undefined): IndexIncompleteReason[];
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { checkpointKind } from './embedding-checkpoint.js';
|
|
2
|
+
import { scopeExtractionFailureTotal } from './ingestion/scope-resolution/scope-extraction-failures.js';
|
|
2
3
|
export const INDEX_INCOMPLETE_REASONS = [
|
|
3
4
|
'incremental-in-progress',
|
|
4
5
|
'embedding-checkpoint-pending',
|
|
5
6
|
'embedding-count-unverified',
|
|
6
7
|
'graph-write-collapsed',
|
|
8
|
+
'scope-extraction-unverified',
|
|
9
|
+
'scope-extraction-failed',
|
|
7
10
|
];
|
|
8
11
|
/**
|
|
9
12
|
* Fraction of the pipeline's relationship count that must survive into the DB
|
|
@@ -105,6 +108,16 @@ export function getIndexIncompleteReasons(meta) {
|
|
|
105
108
|
// from a codebase that genuinely has no such relationships.
|
|
106
109
|
if (meta?.graphWriteCollapsed)
|
|
107
110
|
reasons.push('graph-write-collapsed');
|
|
111
|
+
if (meta?.scopeExtractionReceipt !== 1) {
|
|
112
|
+
reasons.push('scope-extraction-unverified');
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
const total = scopeExtractionFailureTotal(meta.scopeExtractionFailures);
|
|
116
|
+
if (total === undefined)
|
|
117
|
+
reasons.push('scope-extraction-unverified');
|
|
118
|
+
else if (total > 0)
|
|
119
|
+
reasons.push('scope-extraction-failed');
|
|
120
|
+
}
|
|
108
121
|
if (meta?.embeddingCheckpoint) {
|
|
109
122
|
// The three checkpoint kinds are not one operator-facing state. GUARDRAILS
|
|
110
123
|
// and the runbook document `embedding-checkpoint-pending` as "N node(s)
|
|
@@ -31,6 +31,8 @@ export interface WorkerExtractedData {
|
|
|
31
31
|
* finalize-orchestrator.
|
|
32
32
|
*/
|
|
33
33
|
parsedFiles: ParsedFile[];
|
|
34
|
+
/** Scope-extraction omissions represented by this worker/cache result. */
|
|
35
|
+
scopeExtractionFailures: string[];
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
36
38
|
* Merge a list of `ParseWorkerResult`s into the running graph + symbol
|
|
@@ -57,6 +57,7 @@ export const mergeChunkResults = (graph, symbolTable, chunkResults, exportedType
|
|
|
57
57
|
const allORMQueries = [];
|
|
58
58
|
const fileScopeBindingsByFile = [];
|
|
59
59
|
const allParsedFiles = [];
|
|
60
|
+
const scopeExtractionFailures = [];
|
|
60
61
|
for (const result of chunkResults) {
|
|
61
62
|
// Worker jobs and input files are already merged in stable start-index/path
|
|
62
63
|
// order. Canonicalize the final per-result node boundary once so graph
|
|
@@ -123,6 +124,9 @@ export const mergeChunkResults = (graph, symbolTable, chunkResults, exportedType
|
|
|
123
124
|
if (result.parsedFiles)
|
|
124
125
|
for (const item of result.parsedFiles)
|
|
125
126
|
allParsedFiles.push(item);
|
|
127
|
+
for (const filePath of result.scopeExtractionFailures ?? []) {
|
|
128
|
+
scopeExtractionFailures.push(filePath);
|
|
129
|
+
}
|
|
126
130
|
}
|
|
127
131
|
return {
|
|
128
132
|
routes: allRoutes,
|
|
@@ -139,6 +143,7 @@ export const mergeChunkResults = (graph, symbolTable, chunkResults, exportedType
|
|
|
139
143
|
springTypes: allSpringTypes,
|
|
140
144
|
fileScopeBindings: fileScopeBindingsByFile,
|
|
141
145
|
parsedFiles: allParsedFiles,
|
|
146
|
+
scopeExtractionFailures,
|
|
142
147
|
};
|
|
143
148
|
};
|
|
144
149
|
/**
|
|
@@ -99,5 +99,8 @@ export declare function runChunkedParseAndResolve(graph: KnowledgeGraph, scanned
|
|
|
99
99
|
* cache analyze run can skip the dominant `extractParsedFile` cost
|
|
100
100
|
* (otherwise ~58s on a 1000-file repo). */
|
|
101
101
|
parsedFiles: import('../../../_shared/index.js').ParsedFile[];
|
|
102
|
+
scopeExtractionFailures: string[];
|
|
103
|
+
/** Files excluded because their non-standalone language parser was unavailable. */
|
|
104
|
+
unavailableScopeLanguageFiles: number;
|
|
102
105
|
}>;
|
|
103
106
|
export {};
|
|
@@ -366,6 +366,7 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
366
366
|
logger.warn(`Skipping ${count} ${lang} file(s) — ${lang} parser not available (native binding may not have built). Try: npm rebuild tree-sitter-${lang}`);
|
|
367
367
|
}
|
|
368
368
|
}
|
|
369
|
+
const unavailableScopeLanguageFiles = [...skippedByLang.values()].reduce((total, count) => total + count, 0);
|
|
369
370
|
// Sort parseableScanned alphabetically for stable chunk membership
|
|
370
371
|
// across runs (Finding 4). Without this, filesystem-scan order can
|
|
371
372
|
// shift between runs (notably on macOS APFS where directory entry
|
|
@@ -563,6 +564,7 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
563
564
|
// the second-half of the parse-cache speedup since scope-resolution's
|
|
564
565
|
// re-parse otherwise dominates the warm-cache wall-clock time.
|
|
565
566
|
const allParsedFiles = [];
|
|
567
|
+
const scopeExtractionFailures = new Set();
|
|
566
568
|
// Incremental parse cache (Option B): chunk-level content-addressed.
|
|
567
569
|
// When the chunk's (filePath, content-hash) signature matches a prior
|
|
568
570
|
// run's, replay the cached ParseWorkerResult[] instead of dispatching
|
|
@@ -638,6 +640,9 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
638
640
|
// (which was the only path that passed null) is gone.
|
|
639
641
|
const applyChunkResults = async (chunkWorkerData, chunkIdx, chunkFiles, chunkStartMs) => {
|
|
640
642
|
if (chunkWorkerData) {
|
|
643
|
+
for (const filePath of chunkWorkerData.scopeExtractionFailures) {
|
|
644
|
+
scopeExtractionFailures.add(filePath);
|
|
645
|
+
}
|
|
641
646
|
if (chunkWorkerData.parsedFiles?.length) {
|
|
642
647
|
if (parsedFileStorePath) {
|
|
643
648
|
await persistParsedFileChunk(parsedFileStorePath, `chunk-${chunkIdx}`, chunkWorkerData.parsedFiles);
|
|
@@ -1336,5 +1341,7 @@ export async function runChunkedParseAndResolve(graph, scannedFiles, allPaths, t
|
|
|
1336
1341
|
// cache: when the file's ParsedFile is here, scope-resolution skips its own
|
|
1337
1342
|
// `extractParsedFile` call.
|
|
1338
1343
|
parsedFiles: allParsedFiles,
|
|
1344
|
+
scopeExtractionFailures: [...scopeExtractionFailures].sort(),
|
|
1345
|
+
unavailableScopeLanguageFiles,
|
|
1339
1346
|
};
|
|
1340
1347
|
}
|
|
@@ -68,5 +68,9 @@ export interface ParseOutput {
|
|
|
68
68
|
* costing ~58s on a 1000-file repo).
|
|
69
69
|
*/
|
|
70
70
|
readonly parsedFiles: readonly ParsedFile[];
|
|
71
|
+
/** Files whose scope extraction failed while legacy parsing continued. */
|
|
72
|
+
readonly scopeExtractionFailures: readonly string[];
|
|
73
|
+
/** Files omitted because their non-standalone language parser was unavailable. */
|
|
74
|
+
readonly unavailableScopeLanguageFiles: number;
|
|
71
75
|
}
|
|
72
76
|
export declare const parsePhase: PipelinePhase<ParseOutput>;
|
|
@@ -109,10 +109,11 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
109
109
|
graphEmitSink?.close();
|
|
110
110
|
}
|
|
111
111
|
// Extract final results for the PipelineResult contract
|
|
112
|
-
const { totalFiles, usedWorkerPool } = getPhaseOutput(results, 'parse');
|
|
112
|
+
const { totalFiles, usedWorkerPool, unavailableScopeLanguageFiles } = getPhaseOutput(results, 'parse');
|
|
113
113
|
let communityResult;
|
|
114
114
|
let processResult;
|
|
115
115
|
const scopeResolutionOutput = getPhaseOutput(results, 'scopeResolution');
|
|
116
|
+
const scopeExtractionFailures = scopeResolutionOutput.scopeExtractionFailures;
|
|
116
117
|
const resolutionOutcomes = scopeResolutionOutput.resolutionOutcomes;
|
|
117
118
|
const undecidedSatisfaction = scopeResolutionOutput.undecidedSatisfaction;
|
|
118
119
|
// Streamed PDG-emit manifest (#2202): present only when streaming was on.
|
|
@@ -155,6 +156,8 @@ export const runPipelineFromRepo = async (repoPath, onProgress, options) => {
|
|
|
155
156
|
resolutionOutcomes,
|
|
156
157
|
undecidedSatisfaction,
|
|
157
158
|
usedWorkerPool,
|
|
159
|
+
scopeExtractionFailures,
|
|
160
|
+
unavailableScopeLanguageFiles,
|
|
158
161
|
pdgEmitManifest,
|
|
159
162
|
propertyInference,
|
|
160
163
|
};
|
|
@@ -49,8 +49,14 @@ export function extractParsedFile(provider, sourceText, filePath, onWarn, cached
|
|
|
49
49
|
}
|
|
50
50
|
catch (err) {
|
|
51
51
|
const message = `scope extraction failed for ${filePath}: ${err instanceof Error ? err.message : String(err)}`;
|
|
52
|
-
if (onWarn !== undefined)
|
|
53
|
-
|
|
52
|
+
if (onWarn !== undefined) {
|
|
53
|
+
try {
|
|
54
|
+
onWarn(message);
|
|
55
|
+
}
|
|
56
|
+
catch (warnErr) {
|
|
57
|
+
logger.warn(`scope extraction warning callback failed for ${filePath}: ${warnErr instanceof Error ? warnErr.message : String(warnErr)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
54
60
|
logger.warn(message);
|
|
55
61
|
return undefined;
|
|
56
62
|
}
|
|
@@ -40,6 +40,8 @@ export interface ScopeResolutionOutput {
|
|
|
40
40
|
readonly importsEmitted: number;
|
|
41
41
|
/** Reference (CALLS / ACCESSES / INHERITS / USES) edges emitted. */
|
|
42
42
|
readonly referenceEdgesEmitted: number;
|
|
43
|
+
/** Files still missing scope captures after the main-thread fallback. */
|
|
44
|
+
readonly scopeExtractionFailures: readonly string[];
|
|
43
45
|
/** Additive stream of resolver diagnostics; does not affect graph edges. */
|
|
44
46
|
readonly resolutionOutcomes: readonly ResolutionOutcome[];
|
|
45
47
|
/**
|
|
@@ -37,12 +37,14 @@ import { buildFunctionNodeIndex } from '../../taint/summary-harvest-driver.js';
|
|
|
37
37
|
import { buildPropertyNameIndex } from '../passes/unique-name-properties.js';
|
|
38
38
|
import { PdgEmitSink } from '../../../lbug/pdg-emit-sink.js';
|
|
39
39
|
import { resolveNativeSafeStorageDir } from '../../../lbug/lbug-config.js';
|
|
40
|
+
import { reconcileScopeExtractionFailures } from '../scope-extraction-failures.js';
|
|
40
41
|
import { logger } from '../../../logger.js';
|
|
41
42
|
const NOOP_OUTPUT = Object.freeze({
|
|
42
43
|
ran: false,
|
|
43
44
|
filesProcessed: 0,
|
|
44
45
|
importsEmitted: 0,
|
|
45
46
|
referenceEdgesEmitted: 0,
|
|
47
|
+
scopeExtractionFailures: [],
|
|
46
48
|
resolutionOutcomes: [],
|
|
47
49
|
// Deliberately absent, not `[]`: nothing ran, so nothing was decided either.
|
|
48
50
|
perLanguage: new Map(),
|
|
@@ -79,6 +81,7 @@ export const scopeResolutionPhase = {
|
|
|
79
81
|
const { scannedFiles } = getPhaseOutput(deps, 'structure');
|
|
80
82
|
const parseOutput = getPhaseOutput(deps, 'parse');
|
|
81
83
|
const { model, parsedFiles: workerParsedFiles } = parseOutput;
|
|
84
|
+
const scopeExtractionFailures = new Set(parseOutput.scopeExtractionFailures);
|
|
82
85
|
// SemanticModel populated during `parse`: scope-resolution consumes
|
|
83
86
|
// TypeRegistry / MethodRegistry / SymbolTable lookups instead of
|
|
84
87
|
// rebuilding parallel indexes. See ARCHITECTURE.md § "Semantic-model
|
|
@@ -395,6 +398,9 @@ export const scopeResolutionPhase = {
|
|
|
395
398
|
}
|
|
396
399
|
: undefined,
|
|
397
400
|
}, provider);
|
|
401
|
+
// Worker warnings are provisional: scope-resolution retries missing
|
|
402
|
+
// ParsedFiles on the main thread. Persist only final omissions.
|
|
403
|
+
reconcileScopeExtractionFailures(scopeExtractionFailures, files.map((file) => file.path), stats.scopeExtractionFailedPaths);
|
|
398
404
|
// Release file contents and pre-extracted entries after each language
|
|
399
405
|
// to reduce memory pressure. For large codebases (16K+ PHP files),
|
|
400
406
|
// holding all source code simultaneously with scope trees causes OOM.
|
|
@@ -503,13 +509,19 @@ export const scopeResolutionPhase = {
|
|
|
503
509
|
// Even when no language ran, surface a finalized manifest (its CSVs are on
|
|
504
510
|
// disk) so loadGraphToLbug COPYs them rather than orphaning them — empty in
|
|
505
511
|
// the no-files case, harmless.
|
|
506
|
-
if (!anyRan)
|
|
507
|
-
return
|
|
512
|
+
if (!anyRan) {
|
|
513
|
+
return {
|
|
514
|
+
...NOOP_OUTPUT,
|
|
515
|
+
scopeExtractionFailures: [...scopeExtractionFailures].sort(),
|
|
516
|
+
...(pdgEmitManifest ? { pdgEmitManifest } : {}),
|
|
517
|
+
};
|
|
518
|
+
}
|
|
508
519
|
return {
|
|
509
520
|
ran: true,
|
|
510
521
|
filesProcessed: totalFiles,
|
|
511
522
|
importsEmitted: totalImports,
|
|
512
523
|
referenceEdgesEmitted: totalRefs,
|
|
524
|
+
scopeExtractionFailures: [...scopeExtractionFailures].sort(),
|
|
513
525
|
resolutionOutcomes,
|
|
514
526
|
undecidedSatisfaction,
|
|
515
527
|
perLanguage,
|
|
@@ -190,6 +190,8 @@ interface RunScopeResolutionInput {
|
|
|
190
190
|
interface RunScopeResolutionStats {
|
|
191
191
|
readonly filesProcessed: number;
|
|
192
192
|
readonly filesSkipped: number;
|
|
193
|
+
/** Files still missing a ParsedFile after the main-thread fallback. */
|
|
194
|
+
readonly scopeExtractionFailedPaths: readonly string[];
|
|
193
195
|
readonly importsEmitted: number;
|
|
194
196
|
readonly resolve: ResolveStats;
|
|
195
197
|
readonly referenceEdgesEmitted: number;
|
|
@@ -250,6 +250,7 @@ export function runScopeResolution(input, provider) {
|
|
|
250
250
|
};
|
|
251
251
|
// ── Phase 1: extract each file → ParsedFile ────────────────────────────
|
|
252
252
|
const parsedFiles = [];
|
|
253
|
+
const scopeExtractionFailedPaths = [];
|
|
253
254
|
let filesSkipped = 0;
|
|
254
255
|
const treeCache = input.treeCache;
|
|
255
256
|
const preExtracted = input.preExtractedParsedFiles;
|
|
@@ -273,9 +274,15 @@ export function runScopeResolution(input, provider) {
|
|
|
273
274
|
}
|
|
274
275
|
if (parsed === undefined) {
|
|
275
276
|
const cachedTree = treeCache?.get(file.path);
|
|
276
|
-
|
|
277
|
+
let extractionWarned = false;
|
|
278
|
+
parsed = extractParsedFile(provider.languageProvider, file.content, file.path, (warning) => {
|
|
279
|
+
extractionWarned = true;
|
|
280
|
+
onWarn(warning);
|
|
281
|
+
}, cachedTree);
|
|
277
282
|
if (parsed === undefined) {
|
|
278
283
|
filesSkipped++;
|
|
284
|
+
if (extractionWarned)
|
|
285
|
+
scopeExtractionFailedPaths.push(file.path);
|
|
279
286
|
continue;
|
|
280
287
|
}
|
|
281
288
|
}
|
|
@@ -314,6 +321,7 @@ export function runScopeResolution(input, provider) {
|
|
|
314
321
|
return {
|
|
315
322
|
filesProcessed: parsedFiles.length,
|
|
316
323
|
filesSkipped,
|
|
324
|
+
scopeExtractionFailedPaths,
|
|
317
325
|
importsEmitted: 0,
|
|
318
326
|
resolve: { sitesProcessed: 0, referencesEmitted: 0, unresolved: 0 },
|
|
319
327
|
referenceEdgesEmitted: 0,
|
|
@@ -349,6 +357,7 @@ export function runScopeResolution(input, provider) {
|
|
|
349
357
|
return {
|
|
350
358
|
filesProcessed: 0,
|
|
351
359
|
filesSkipped,
|
|
360
|
+
scopeExtractionFailedPaths,
|
|
352
361
|
importsEmitted: 0,
|
|
353
362
|
resolve: { sitesProcessed: 0, referencesEmitted: 0, unresolved: 0 },
|
|
354
363
|
referenceEdgesEmitted: 0,
|
|
@@ -1136,6 +1145,7 @@ export function runScopeResolution(input, provider) {
|
|
|
1136
1145
|
return {
|
|
1137
1146
|
filesProcessed: parsedFiles.length,
|
|
1138
1147
|
filesSkipped,
|
|
1148
|
+
scopeExtractionFailedPaths,
|
|
1139
1149
|
importsEmitted,
|
|
1140
1150
|
resolve: resolveStats,
|
|
1141
1151
|
referenceEdgesEmitted: emitted +
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ScopeExtractionFailureSummary {
|
|
2
|
+
/** Exact number of unique files whose scope extraction failed. */
|
|
3
|
+
readonly total: number;
|
|
4
|
+
/** Deterministic sample of repo-relative paths for diagnostics. */
|
|
5
|
+
readonly paths: readonly string[];
|
|
6
|
+
/** True when `paths` is a capped sample rather than the full set. */
|
|
7
|
+
readonly truncated?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const SCOPE_EXTRACTION_FAILURE_PATH_LIMIT = 25;
|
|
10
|
+
/** Read a persisted summary without trusting its runtime JSON shape. */
|
|
11
|
+
export declare function scopeExtractionFailureTotal(summary: unknown): number | undefined;
|
|
12
|
+
/** Replace provisional worker failures with the final fallback outcome. */
|
|
13
|
+
export declare function reconcileScopeExtractionFailures(failures: Set<string>, attemptedPaths: readonly string[], failedPaths: readonly string[]): void;
|
|
14
|
+
export declare function summarizeScopeExtractionFailures(paths?: readonly string[], limit?: number): ScopeExtractionFailureSummary | undefined;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export const SCOPE_EXTRACTION_FAILURE_PATH_LIMIT = 25;
|
|
2
|
+
/** Read a persisted summary without trusting its runtime JSON shape. */
|
|
3
|
+
export function scopeExtractionFailureTotal(summary) {
|
|
4
|
+
if (summary === undefined)
|
|
5
|
+
return 0;
|
|
6
|
+
if (typeof summary !== 'object' || summary === null)
|
|
7
|
+
return undefined;
|
|
8
|
+
const total = summary.total;
|
|
9
|
+
if (total === 0)
|
|
10
|
+
return 0;
|
|
11
|
+
return typeof total === 'number' && Number.isInteger(total) && total > 0 ? total : undefined;
|
|
12
|
+
}
|
|
13
|
+
/** Replace provisional worker failures with the final fallback outcome. */
|
|
14
|
+
export function reconcileScopeExtractionFailures(failures, attemptedPaths, failedPaths) {
|
|
15
|
+
const stillFailed = new Set(failedPaths);
|
|
16
|
+
for (const filePath of attemptedPaths) {
|
|
17
|
+
if (stillFailed.has(filePath))
|
|
18
|
+
failures.add(filePath);
|
|
19
|
+
else
|
|
20
|
+
failures.delete(filePath);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
export function summarizeScopeExtractionFailures(paths = [], limit = SCOPE_EXTRACTION_FAILURE_PATH_LIMIT) {
|
|
24
|
+
const unique = [
|
|
25
|
+
...new Set(paths.filter((path) => typeof path === 'string' && path.length > 0)),
|
|
26
|
+
].sort();
|
|
27
|
+
if (unique.length === 0)
|
|
28
|
+
return undefined;
|
|
29
|
+
const boundedLimit = Number.isInteger(limit) && limit >= 0 ? limit : SCOPE_EXTRACTION_FAILURE_PATH_LIMIT;
|
|
30
|
+
return {
|
|
31
|
+
total: unique.length,
|
|
32
|
+
paths: unique.slice(0, boundedLimit),
|
|
33
|
+
...(unique.length > boundedLimit ? { truncated: true } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -270,6 +270,12 @@ export interface ParseWorkerResult {
|
|
|
270
270
|
* finalize-orchestrator.
|
|
271
271
|
*/
|
|
272
272
|
parsedFiles: ParsedFile[];
|
|
273
|
+
/**
|
|
274
|
+
* Repo-relative paths whose scope-capture/extraction step threw. Optional for
|
|
275
|
+
* parse-cache compatibility; unlike transient worker telemetry this must be
|
|
276
|
+
* replayed on a cache hit so the persisted index cannot claim completeness.
|
|
277
|
+
*/
|
|
278
|
+
scopeExtractionFailures?: string[];
|
|
273
279
|
skippedLanguages: Record<string, number>;
|
|
274
280
|
/**
|
|
275
281
|
* Files whose parse output carried a value the structured-clone algorithm
|
|
@@ -1039,7 +1039,13 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
1039
1039
|
// see parsedfile-store.ts). parse-impl flushes `result.parsedFiles` to disk
|
|
1040
1040
|
// per chunk and does NOT retain them in main-thread heap, so this no longer
|
|
1041
1041
|
// costs ~1× the semantic model in RAM during parse.
|
|
1042
|
-
|
|
1042
|
+
let scopeExtractionFailed = false;
|
|
1043
|
+
const parsedFile = extractParsedFile(provider, parseContent, file.path, (message) => {
|
|
1044
|
+
scopeExtractionFailed = true;
|
|
1045
|
+
reportWarning(message);
|
|
1046
|
+
}, tree, scopeSourceKind);
|
|
1047
|
+
if (scopeExtractionFailed)
|
|
1048
|
+
(result.scopeExtractionFailures ??= []).push(file.path);
|
|
1043
1049
|
if (parsedFile !== undefined) {
|
|
1044
1050
|
// Capture-time side-channel (#1983): `extractParsedFile` just ran the
|
|
1045
1051
|
// provider's `emitScopeCaptures`, which (for C++ ADL/namespace marks,
|
|
@@ -45,11 +45,14 @@ export const mergeResult = (target, src) => {
|
|
|
45
45
|
appendAll(target.constructorBindings, src.constructorBindings);
|
|
46
46
|
appendAll(target.fileScopeBindings, src.fileScopeBindings);
|
|
47
47
|
appendAll(target.parsedFiles, src.parsedFiles);
|
|
48
|
+
if (src.scopeExtractionFailures && src.scopeExtractionFailures.length > 0) {
|
|
49
|
+
appendAll((target.scopeExtractionFailures ??= []), src.scopeExtractionFailures);
|
|
50
|
+
}
|
|
48
51
|
for (const [lang, count] of Object.entries(src.skippedLanguages)) {
|
|
49
52
|
target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
|
|
50
53
|
}
|
|
51
54
|
if (src.skippedPaths && src.skippedPaths.length > 0) {
|
|
52
|
-
(target.skippedPaths ??= [])
|
|
55
|
+
appendAll((target.skippedPaths ??= []), src.skippedPaths);
|
|
53
56
|
}
|
|
54
57
|
target.fileCount += src.fileCount;
|
|
55
58
|
};
|
package/dist/core/run-analyze.js
CHANGED
|
@@ -18,6 +18,7 @@ import { acquireIndexLock } from '../storage/index-lock.js';
|
|
|
18
18
|
import { runPipelineFromRepo } from './ingestion/pipeline.js';
|
|
19
19
|
import { logUnresolvedReceiverFiles, summarizeUnresolvedReceivers, } from './ingestion/scope-resolution/unresolved-receivers.js';
|
|
20
20
|
import { summarizeUndecidedSatisfaction } from './ingestion/scope-resolution/undecided-satisfaction.js';
|
|
21
|
+
import { summarizeScopeExtractionFailures } from './ingestion/scope-resolution/scope-extraction-failures.js';
|
|
21
22
|
import { resetDegradedParseCounter } from './tree-sitter/safe-parse.js';
|
|
22
23
|
import { initLbug, loadGraphToLbug, getLbugStats, executeQuery, executeWithReusedStatement, closeLbug, closeLbugBeforeExit, loadCachedEmbeddings, deleteNodesForFiles, ensureEmbeddingRowDmlSafe, ensureFtsRowDmlSafe, readIndexCatalogSnapshot, INDEX_CATALOG_UNREADABLE, deleteAllCommunitiesAndProcesses, deleteAllInterprocTaintPaths, deleteAllCallSummaries, deleteAllInjects, deleteAllAdvisedBy, deleteSpringAopEvidenceNodes, deleteSpringAutoConfigurationDeclarations, deleteSpringAutoConfigurationSyntheticClasses, queryImportersBatch, loadFTSExtension, wipeLbugDbFiles, LbugWipeError, DELETE_FILES_CHUNK_SIZE, } from './lbug/lbug-adapter.js';
|
|
23
24
|
import { estimateBufferPool, setBufferPoolSizeHint, resolveNativeSafeStorageDir, } from './lbug/lbug-config.js';
|
|
@@ -2802,6 +2803,11 @@ async function runFullAnalysisInner(repoPath, options, callbacks, writeTarget, r
|
|
|
2802
2803
|
// Git-only: non-git repos never take the incremental path.
|
|
2803
2804
|
schemaFingerprint: hasGitDir(repoPath) ? SCHEMA_FINGERPRINT : undefined,
|
|
2804
2805
|
unresolvedReceiverMembers: summarizeUnresolvedReceivers(resolutionOutcomes),
|
|
2806
|
+
scopeExtractionFailures: summarizeScopeExtractionFailures(pipelineResult.scopeExtractionFailures),
|
|
2807
|
+
// A receipt certifies that every scope-capable source file was inspected.
|
|
2808
|
+
// Optional grammars may be unavailable by design; omitting the receipt in
|
|
2809
|
+
// that case makes readers report an unverified lower bound.
|
|
2810
|
+
scopeExtractionReceipt: pipelineResult.unavailableScopeLanguageFiles === 0 ? 1 : undefined,
|
|
2805
2811
|
// Carried forward ONLY when this run could not measure — `saveMeta` writes
|
|
2806
2812
|
// a fresh object, so omitting the key deletes a prior record and turns a
|
|
2807
2813
|
// hedged answer back into a confident one. A run that DID measure always
|
|
@@ -87,6 +87,8 @@ export interface CodebaseContext {
|
|
|
87
87
|
* number of SENTENCES, which has no relation to how much is missing.
|
|
88
88
|
*/
|
|
89
89
|
export interface EpistemicCauses {
|
|
90
|
+
/** Files whose scope-extraction output is absent from this index. */
|
|
91
|
+
readonly scopeExtractionFiles: number;
|
|
90
92
|
/**
|
|
91
93
|
* Call SITES dropped at index time because the receiver's type could not be
|
|
92
94
|
* established. Unit: call sites, taken from the index's
|
|
@@ -57,6 +57,7 @@ import { decodeReachingDefReason } from '../../core/ingestion/cfg/reaching-def-r
|
|
|
57
57
|
import { EXTENSIONS } from '../../core/ingestion/import-resolvers/utils.js';
|
|
58
58
|
import { compareCodeUnits } from '../../lib/utils.js';
|
|
59
59
|
import { lookupExternalCallCount, lookupUnresolvedCallCount, } from '../../core/ingestion/scope-resolution/unresolved-receivers.js';
|
|
60
|
+
import { scopeExtractionFailureTotal } from '../../core/ingestion/scope-resolution/scope-extraction-failures.js';
|
|
60
61
|
import { lookupCount } from '../../core/ingestion/scope-resolution/summary-maps.js';
|
|
61
62
|
import { DEFERRED_IMPORT_REASON_SUFFIX, TYPE_ONLY_IMPORT_REASON_SUFFIX, } from '../../core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js';
|
|
62
63
|
import { fnLineOf, isPdgDegradedLayerStatus, makePdgImpactErrorResult, makePdgLayerDegradedResult, pdgLayerStatus, pdgStampForMode, runImpactPDG, validateImpactMode, pdgBridgeEvidenceForImpact, betterBridgeEvidence, composeUnifiedPdgImpactResult, splitCalleeIds, } from './pdg-impact.js';
|
|
@@ -464,6 +465,7 @@ function epistemicFrom(dropped) {
|
|
|
464
465
|
? {
|
|
465
466
|
epistemic: 'exact',
|
|
466
467
|
causes: {
|
|
468
|
+
scopeExtractionFiles: dropped.scopeExtraction,
|
|
467
469
|
receiverTyping: 0,
|
|
468
470
|
dispatchBoundary: dropped.dispatch,
|
|
469
471
|
externalBoundary: dropped.external,
|
|
@@ -479,6 +481,7 @@ function epistemicFrom(dropped) {
|
|
|
479
481
|
// prose saying `2 call sites` — a consumer branching on the number
|
|
480
482
|
// would read a different magnitude than the human reading the text.
|
|
481
483
|
causes: {
|
|
484
|
+
scopeExtractionFiles: dropped.scopeExtraction,
|
|
482
485
|
receiverTyping: dropped.sites,
|
|
483
486
|
dispatchBoundary: dropped.dispatch,
|
|
484
487
|
externalBoundary: dropped.external,
|
|
@@ -486,6 +489,28 @@ function epistemicFrom(dropped) {
|
|
|
486
489
|
},
|
|
487
490
|
};
|
|
488
491
|
}
|
|
492
|
+
function scopeExtractionBoundaries(summary, receipt) {
|
|
493
|
+
const unknown = {
|
|
494
|
+
notes: [
|
|
495
|
+
'Scope-extraction completeness was not recorded for this index, so actual impact may be higher.',
|
|
496
|
+
],
|
|
497
|
+
files: 0,
|
|
498
|
+
};
|
|
499
|
+
if (receipt !== 1)
|
|
500
|
+
return unknown;
|
|
501
|
+
const total = scopeExtractionFailureTotal(summary);
|
|
502
|
+
if (total === undefined)
|
|
503
|
+
return unknown;
|
|
504
|
+
if (total === 0)
|
|
505
|
+
return { notes: [], files: 0 };
|
|
506
|
+
return {
|
|
507
|
+
notes: [
|
|
508
|
+
`Scope extraction failed for ${total} ${total === 1 ? 'file' : 'files'} while this index was built. ` +
|
|
509
|
+
`Scope-resolution edges from ${total === 1 ? 'that file are' : 'those files are'} absent, so actual impact may be higher.`,
|
|
510
|
+
],
|
|
511
|
+
files: total,
|
|
512
|
+
};
|
|
513
|
+
}
|
|
489
514
|
/**
|
|
490
515
|
* Boundary notes for call sites the analyzer dropped because it could not type
|
|
491
516
|
* their receiver, when the queried symbol's name is among them (#2744).
|
|
@@ -5561,6 +5586,7 @@ export class LocalBackend {
|
|
|
5561
5586
|
meta = undefined;
|
|
5562
5587
|
}
|
|
5563
5588
|
const receiverDrops = unresolvedReceiverBoundaries(meta?.unresolvedReceiverMembers, symName);
|
|
5589
|
+
const scopeExtractionDrops = scopeExtractionBoundaries(meta?.scopeExtractionFailures, meta?.scopeExtractionReceipt);
|
|
5564
5590
|
// #2873 — satisfaction checks the analyzer never completed. Read on the
|
|
5565
5591
|
// same footing as the receiver drops, and BEFORE the heritage probe for the
|
|
5566
5592
|
// same reason: this cause leaves no edge for that probe to find, so a
|
|
@@ -5592,6 +5618,7 @@ export class LocalBackend {
|
|
|
5592
5618
|
...receiverDrops,
|
|
5593
5619
|
notes: [
|
|
5594
5620
|
...receiverDrops.notes,
|
|
5621
|
+
...scopeExtractionDrops.notes,
|
|
5595
5622
|
...undecidedDrops.notes,
|
|
5596
5623
|
...(convexDispatch === undefined ? [] : [convexDispatch.boundary]),
|
|
5597
5624
|
],
|
|
@@ -5600,6 +5627,7 @@ export class LocalBackend {
|
|
|
5600
5627
|
// count of omitted symbols. Keep the magnitude at zero rather than
|
|
5601
5628
|
// inventing one from the presence of a note.
|
|
5602
5629
|
dispatch: 0,
|
|
5630
|
+
scopeExtraction: scopeExtractionDrops.files,
|
|
5603
5631
|
};
|
|
5604
5632
|
try {
|
|
5605
5633
|
// Discover the interface / abstract supertypes on the target's boundary.
|
|
@@ -5675,6 +5703,7 @@ export class LocalBackend {
|
|
|
5675
5703
|
epistemic: 'lower-bound',
|
|
5676
5704
|
boundaries: [...droppedBoundaries.notes, ...boundaries],
|
|
5677
5705
|
causes: {
|
|
5706
|
+
scopeExtractionFiles: droppedBoundaries.scopeExtraction,
|
|
5678
5707
|
receiverTyping: droppedBoundaries.sites,
|
|
5679
5708
|
dispatchBoundary: droppedBoundaries.dispatch + dispatchBoundarySymbols,
|
|
5680
5709
|
externalBoundary: droppedBoundaries.external,
|
package/dist/mcp/tools.js
CHANGED
|
@@ -248,13 +248,14 @@ NOTE: ACCESSES edges (field read/write tracking) are included in context results
|
|
|
248
248
|
COMPLETENESS OF incoming: alongside symbol/incoming/outgoing the result carries the same epistemic envelope impact() returns:
|
|
249
249
|
- epistemic: 'exact' | 'lower-bound' — 'lower-bound' means callers exist that this view provably does not list.
|
|
250
250
|
- boundaries: string[] — one plain-language sentence per reason. Prose for humans; branch on causes instead.
|
|
251
|
-
- causes: { receiverTyping, dispatchBoundary, externalBoundary, undecidedSatisfaction } — machine-readable WHY. Every field counts MISSING THINGS, never sentences:
|
|
251
|
+
- causes: { scopeExtractionFiles, receiverTyping, dispatchBoundary, externalBoundary, undecidedSatisfaction } — machine-readable WHY. Every field counts MISSING THINGS, never sentences:
|
|
252
|
+
- causes.scopeExtractionFiles (unit: files) > 0 — scope extraction still failed after the fallback pass, so scope-resolution edges from those files are absent. A value of 0 does not prove completeness when epistemic is 'lower-bound' because an older or unverified index has no measured file count. Re-run \`gitnexus analyze --force\`; if the reason persists, inspect the extraction warnings.
|
|
252
253
|
- causes.receiverTyping (unit: call sites) > 0 — RESOLVER GAP: the analyzer dropped that many call sites on this name because it could not type the receiver, so they are missing from incoming. Do not read an absent caller as proof none exists.
|
|
253
254
|
- causes.externalBoundary (unit: call sites) > 0 — the calls left the indexed program (System.out.println, fetch(...)). NOT a defect: no in-graph node could have been reached. An epistemic:'exact' result can carry this.
|
|
254
255
|
- causes.dispatchBoundary (unit: symbols) > 0 — DI or interface dispatch: that many symbols sit on or beyond a boundary static analysis cannot cross. Irreducible. A symbol count, not a site count — per-site multiplicity is not retained for these edges — so compare its magnitude with receiverTyping, not its exact value. A framework runtime-proxy boundary can make epistemic lower-bound while this value remains 0 because endpoint metadata proves the gap but cannot count omitted symbols.
|
|
255
256
|
- causes.undecidedSatisfaction (unit: unjudged interface/type pairs) > 0 — the analyzer could not decide whether a type satisfies an interface, so no IMPLEMENTS edge exists and no dispatch boundary was left for the walk to notice. Usually fixable by making the missing dependency available to analysis.
|
|
256
257
|
|
|
257
|
-
REQUIRES RE-INDEX: causes.receiverTyping, causes.externalBoundary, causes.undecidedSatisfaction, and framework runtime-proxy boundary detection depend on index-time metadata that only a current analyzer writes. Against an older index the metadata can be absent, which is indistinguishable from "nothing was dropped" unless the schema probe detects the stale index — re-run \`gitnexus analyze\` before trusting a zero or an apparently exact result.
|
|
258
|
+
REQUIRES RE-INDEX: causes.scopeExtractionFiles, causes.receiverTyping, causes.externalBoundary, causes.undecidedSatisfaction, and framework runtime-proxy boundary detection depend on index-time metadata that only a current analyzer writes. Against an older index the metadata can be absent, which is indistinguishable from "nothing was dropped" unless the schema probe detects the stale index — re-run \`gitnexus analyze\` before trusting a zero or an apparently exact result.
|
|
258
259
|
|
|
259
260
|
GROUP MODE: set "repo" to "@<groupName>" to run context in each member repo (aggregated list), or "@<groupName>/<groupRepoPath>" for one member. If you use "@<groupName>" only, the member defaults to the lexicographically first key in group.yaml "repos".
|
|
260
261
|
|
|
@@ -438,14 +439,15 @@ Output includes:
|
|
|
438
439
|
- 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.
|
|
439
440
|
- epistemic: 'exact' | 'lower-bound' — whether impactedCount is the whole story. 'lower-bound' means the walk provably missed callers, so the count is a floor. Absent only on skipped probes (ambiguous-candidate lists, group fan-out).
|
|
440
441
|
- boundaries: string[] — one plain-language sentence per reason the count is short. Prose for humans; branch on causes instead.
|
|
441
|
-
- causes: { receiverTyping, dispatchBoundary, externalBoundary, undecidedSatisfaction } — the machine-readable split of WHY, so an agent gating its own edits can tell a fixable analyzer gap from an irreducible one. Every field counts MISSING THINGS, never sentences:
|
|
442
|
+
- causes: { scopeExtractionFiles, receiverTyping, dispatchBoundary, externalBoundary, undecidedSatisfaction } — the machine-readable split of WHY, so an agent gating its own edits can tell a fixable analyzer gap from an irreducible one. Every field counts MISSING THINGS, never sentences:
|
|
443
|
+
- causes.scopeExtractionFiles (unit: files) > 0 — scope extraction still failed after the fallback pass, so scope-resolution edges from those files are absent. A value of 0 does not prove completeness when epistemic is 'lower-bound' because an older or unverified index has no measured file count. Re-run \`gitnexus analyze --force\`; if the reason persists, inspect the extraction warnings.
|
|
442
444
|
- causes.receiverTyping (unit: call sites) > 0 — the RESOLVER GAP signal: the analyzer dropped that many call sites because it could not establish the receiver's type (unresolved constructor, factory, chained expression). Those callers are absent from byDepth. Treat the result as incomplete: grep the symbol name before deleting or renaming.
|
|
443
445
|
- causes.externalBoundary (unit: call sites) > 0 — those calls left the indexed program (System.out.println, fetch(...), os.environ.*). NOT a defect and NOT a reason the count is short: there is no in-graph node any edge could have reached. An epistemic:'exact' result can carry this.
|
|
444
446
|
- causes.dispatchBoundary (unit: symbols) > 0 — DI or interface dispatch: that many symbols sit on or beyond a boundary a static walk cannot cross. Irreducible. A symbol count, not a site count — per-site multiplicity is not retained for these edges — so compare its magnitude with receiverTyping, not its exact value. A framework runtime-proxy boundary can make epistemic lower-bound while this value remains 0 because endpoint metadata proves the gap but cannot count omitted symbols.
|
|
445
447
|
|
|
446
448
|
- causes.undecidedSatisfaction (unit: unjudged interface/type pairs) > 0 — the analyzer could not DECIDE whether a type satisfies an interface (a type in a required signature named a package it could not resolve), so no IMPLEMENTS edge exists and no dispatch boundary was left for the walk to notice. Distinct from every cause above, which count decided facts that could not be attributed; this one counts questions never answered. It is the only cause that shortens a result WITHOUT leaving a trace in the graph, so an unhedged zero on a symbol reached only through such an interface would otherwise read as 'nobody calls this'. Usually fixable: it most often means a dependency is missing from the analyzed tree.
|
|
447
449
|
|
|
448
|
-
REQUIRES RE-INDEX: causes.receiverTyping, causes.externalBoundary, causes.undecidedSatisfaction, and framework runtime-proxy boundary detection depend on index-time metadata that only a current analyzer writes. Against an older index the metadata can be absent, which is indistinguishable from "nothing was dropped" unless the schema probe detects the stale index — re-run \`gitnexus analyze\` before trusting a zero or an apparently exact result.
|
|
450
|
+
REQUIRES RE-INDEX: causes.scopeExtractionFiles, causes.receiverTyping, causes.externalBoundary, causes.undecidedSatisfaction, and framework runtime-proxy boundary detection depend on index-time metadata that only a current analyzer writes. Against an older index the metadata can be absent, which is indistinguishable from "nothing was dropped" unless the schema probe detects the stale index — re-run \`gitnexus analyze\` before trusting a zero or an apparently exact result.
|
|
449
451
|
|
|
450
452
|
Depth groups:
|
|
451
453
|
- d=1: WILL BREAK (direct callers/importers)
|
|
@@ -621,11 +621,11 @@ import { fileURLToPath } from 'url';
|
|
|
621
621
|
// the re-check line below is for, and why it says AT MERGE rather than
|
|
622
622
|
// when you pick the number.
|
|
623
623
|
//
|
|
624
|
-
//
|
|
625
|
-
//
|
|
626
|
-
//
|
|
627
|
-
//
|
|
628
|
-
//
|
|
624
|
+
// 78 was claimed concurrently by #3060 while this branch was in review. Both
|
|
625
|
+
// branches keep the same package version, so sharing 78 would replay
|
|
626
|
+
// incompatible worker output without a textual merge conflict. This branch
|
|
627
|
+
// therefore takes 79, the next free value above origin/main and every open PR
|
|
628
|
+
// found by the contents-API scan at their exact head SHAs.
|
|
629
629
|
//
|
|
630
630
|
// WHY THIS IS STILL A HAND-PICKED NUMBER, when `SCHEMA_FINGERPRINT` next door
|
|
631
631
|
// is a derived sha256 that cannot collide. The derivation exists and already
|
|
@@ -645,7 +645,7 @@ import { fileURLToPath } from 'url';
|
|
|
645
645
|
// `route-extractors/` and `workers/` module content — would close the missing-
|
|
646
646
|
// bump axis without invalidating on unrelated churn, and is the real follow-up.
|
|
647
647
|
// RE-CHECK AGAINST origin/main AND OPEN PRs IMMEDIATELY BEFORE MERGING.
|
|
648
|
-
const SCHEMA_BUMP =
|
|
648
|
+
const SCHEMA_BUMP = 79;
|
|
649
649
|
const GITNEXUS_PKG_VERSION = (() => {
|
|
650
650
|
try {
|
|
651
651
|
// package.json sits at gitnexus/package.json — two levels up from
|
|
@@ -21,11 +21,12 @@
|
|
|
21
21
|
* `isMissingFilesystemError`) so every existing import site keeps working
|
|
22
22
|
* unchanged.
|
|
23
23
|
*
|
|
24
|
-
* Imports `node:fs`/`node:path` and
|
|
24
|
+
* Imports `node:fs`/`node:path` and a few type-only summary shapes. Keep it that way: a
|
|
25
25
|
* value import here would land in every consumer of `storage/`.
|
|
26
26
|
*/
|
|
27
27
|
import type { UnresolvedReceiverSummary } from '../core/ingestion/scope-resolution/unresolved-receivers.js';
|
|
28
28
|
import type { UndecidedSatisfactionSummary } from '../core/ingestion/scope-resolution/undecided-satisfaction.js';
|
|
29
|
+
import type { ScopeExtractionFailureSummary } from '../core/ingestion/scope-resolution/scope-extraction-failures.js';
|
|
29
30
|
/** The `.gitnexus` directory name, relative to a repo root. */
|
|
30
31
|
export declare const GITNEXUS_DIR = ".gitnexus";
|
|
31
32
|
export declare const INDEX_METADATA_FILE = "gitnexus.json";
|
|
@@ -240,6 +241,21 @@ export interface RepoMeta {
|
|
|
240
241
|
* this adds no runtime dependency from storage/ on core/.
|
|
241
242
|
*/
|
|
242
243
|
unresolvedReceiverMembers?: UnresolvedReceiverSummary;
|
|
244
|
+
/**
|
|
245
|
+
* Files omitted from scope-resolution because their provider capture or
|
|
246
|
+
* extraction step threw. The rest of each file may still be present in the
|
|
247
|
+
* graph, so this is an index-completeness signal rather than a parse failure.
|
|
248
|
+
* Absent means the successful run recorded no such omission; older indexes
|
|
249
|
+
* also read as absent until re-analyzed.
|
|
250
|
+
*/
|
|
251
|
+
scopeExtractionFailures?: ScopeExtractionFailureSummary;
|
|
252
|
+
/**
|
|
253
|
+
* Completeness receipt for scope extraction in the successful run represented
|
|
254
|
+
* by this metadata. A missing or different value means completeness is
|
|
255
|
+
* unknown (legacy, malformed, or unreadable metadata), not that zero files
|
|
256
|
+
* were omitted.
|
|
257
|
+
*/
|
|
258
|
+
scopeExtractionReceipt?: 1;
|
|
243
259
|
/**
|
|
244
260
|
* Interfaces whose structural-satisfaction check this run could not COMPLETE
|
|
245
261
|
* (#2873) — not interfaces found to have no implementors.
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* `isMissingFilesystemError`) so every existing import site keeps working
|
|
22
22
|
* unchanged.
|
|
23
23
|
*
|
|
24
|
-
* Imports `node:fs`/`node:path` and
|
|
24
|
+
* Imports `node:fs`/`node:path` and a few type-only summary shapes. Keep it that way: a
|
|
25
25
|
* value import here would land in every consumer of `storage/`.
|
|
26
26
|
*/
|
|
27
27
|
import fs from 'fs/promises';
|
package/dist/types/pipeline.d.ts
CHANGED
|
@@ -38,6 +38,10 @@ export interface PipelineResult {
|
|
|
38
38
|
* affordance so regression suites can prove the pool engaged.
|
|
39
39
|
*/
|
|
40
40
|
usedWorkerPool: boolean;
|
|
41
|
+
/** Files omitted from scope-resolution while the rest of analysis continued. */
|
|
42
|
+
scopeExtractionFailures: readonly string[];
|
|
43
|
+
/** Files scope resolution could not inspect because their parser was unavailable. */
|
|
44
|
+
unavailableScopeLanguageFiles: number;
|
|
41
45
|
/**
|
|
42
46
|
* Streamed PDG-emit COPY manifest (#2202). Present only when streaming/chunked
|
|
43
47
|
* PDG emit was active (full rebuild + `--pdg` + enabled): the BasicBlock node
|
package/package.json
CHANGED