gitnexus 1.6.10-rc.219 → 1.6.10-rc.220
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/ingestion/language-provider.d.ts +18 -0
- package/dist/core/ingestion/language-provider.js +16 -0
- package/dist/core/ingestion/languages/typescript/convex-endpoint-metadata.d.ts +7 -0
- package/dist/core/ingestion/languages/typescript/convex-endpoint-metadata.js +92 -0
- package/dist/core/ingestion/languages/typescript.js +3 -0
- package/dist/core/ingestion/workers/parse-worker.js +18 -7
- package/dist/core/lbug/csv-generator.js +9 -3
- package/dist/core/lbug/lbug-adapter.js +30 -0
- package/dist/core/lbug/schema.d.ts +2 -2
- package/dist/core/lbug/schema.js +13 -1
- package/dist/mcp/local/convex-metadata.d.ts +8 -0
- package/dist/mcp/local/convex-metadata.js +45 -0
- package/dist/mcp/local/local-backend.d.ts +6 -3
- package/dist/mcp/local/local-backend.js +25 -11
- package/dist/mcp/tools.js +5 -4
- package/dist/storage/parse-cache.js +10 -1
- package/package.json +1 -1
|
@@ -28,6 +28,20 @@ import type Parser from 'tree-sitter';
|
|
|
28
28
|
import type { ExtractedDecoratorRoute } from './workers/parse-worker.js';
|
|
29
29
|
/** Tree-sitter query captures: capture name → AST node (or undefined if not captured). */
|
|
30
30
|
export type CaptureMap = Record<string, SyntaxNode | undefined>;
|
|
31
|
+
export interface DefinitionPropertiesContext {
|
|
32
|
+
readonly nodeLabel: NodeLabel;
|
|
33
|
+
readonly nodeName: string;
|
|
34
|
+
readonly definitionNode: SyntaxNode;
|
|
35
|
+
readonly parsedImports: readonly ParsedImport[];
|
|
36
|
+
readonly isExported: boolean;
|
|
37
|
+
}
|
|
38
|
+
export type DefinitionPropertiesExtractor = (context: DefinitionPropertiesContext) => Readonly<Record<string, unknown>> | undefined;
|
|
39
|
+
/** Run optional provider enrichment without allowing one hook failure to drop
|
|
40
|
+
* the rest of the worker's language batch. */
|
|
41
|
+
export declare function runDefinitionPropertiesExtractor(extractor: DefinitionPropertiesExtractor, context: DefinitionPropertiesContext, onError: (error: unknown) => void): Readonly<Record<string, unknown>> | undefined;
|
|
42
|
+
/** Provider metadata is additive; graph identity and source-location fields
|
|
43
|
+
* supplied by the worker remain authoritative. */
|
|
44
|
+
export declare function mergeCanonicalDefinitionProperties<TCanonical extends Readonly<Record<string, unknown>>>(providerProperties: Readonly<Record<string, unknown>>, canonicalProperties: TCanonical): Record<string, unknown> & TCanonical;
|
|
31
45
|
/** Configuration for AST-based framework detection patterns. */
|
|
32
46
|
export interface AstFrameworkPatternConfig {
|
|
33
47
|
framework: string;
|
|
@@ -215,6 +229,10 @@ interface LanguageProviderConfig {
|
|
|
215
229
|
* constant, and static declarations. Produces VariableInfo with type, visibility,
|
|
216
230
|
* isConst, isStatic, isMutable metadata. Default: undefined (no variable extraction). */
|
|
217
231
|
readonly variableExtractor?: VariableExtractor;
|
|
232
|
+
/** Add language-owned, structured properties to a definition node. Values
|
|
233
|
+
* cross the worker boundary and must therefore be structured-clone-safe.
|
|
234
|
+
* Shared ingestion code treats these properties as opaque. */
|
|
235
|
+
readonly definitionPropertiesExtractor?: DefinitionPropertiesExtractor;
|
|
218
236
|
/** Class/type extractor for deriving canonical qualified names for class-like symbols.
|
|
219
237
|
* Uses the same provider-driven strategy pattern as method/field extraction so
|
|
220
238
|
* namespace/package/module rules stay language-specific. */
|
|
@@ -8,6 +8,22 @@
|
|
|
8
8
|
* The providers table in `languages/index.ts` uses `satisfies Record<SupportedLanguages, LanguageProvider>`
|
|
9
9
|
* so adding a language to the enum without creating a provider is a compiler error.
|
|
10
10
|
*/
|
|
11
|
+
/** Run optional provider enrichment without allowing one hook failure to drop
|
|
12
|
+
* the rest of the worker's language batch. */
|
|
13
|
+
export function runDefinitionPropertiesExtractor(extractor, context, onError) {
|
|
14
|
+
try {
|
|
15
|
+
return extractor(context);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
onError(error);
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Provider metadata is additive; graph identity and source-location fields
|
|
23
|
+
* supplied by the worker remain authoritative. */
|
|
24
|
+
export function mergeCanonicalDefinitionProperties(providerProperties, canonicalProperties) {
|
|
25
|
+
return { ...providerProperties, ...canonicalProperties };
|
|
26
|
+
}
|
|
11
27
|
/**
|
|
12
28
|
* Everything a language needs to provide.
|
|
13
29
|
* Required fields must be explicitly set; optional fields have defaults
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { DefinitionPropertiesContext } from '../../language-provider.js';
|
|
2
|
+
/**
|
|
3
|
+
* Stamp Convex runtime-dispatch metadata only when both the declaration shape
|
|
4
|
+
* and the factory import provenance are known. The MCP layer consumes the
|
|
5
|
+
* resulting property without reparsing lossy FTS text.
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractConvexEndpointProperties(context: DefinitionPropertiesContext): Readonly<Record<string, unknown>> | undefined;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { assertCloneable } from '../../workers/clone-safety.js';
|
|
2
|
+
const GENERATED_ENDPOINT_FACTORIES = new Set([
|
|
3
|
+
'query',
|
|
4
|
+
'mutation',
|
|
5
|
+
'action',
|
|
6
|
+
'internalQuery',
|
|
7
|
+
'internalMutation',
|
|
8
|
+
'internalAction',
|
|
9
|
+
'httpAction',
|
|
10
|
+
]);
|
|
11
|
+
const GENERIC_ENDPOINT_FACTORIES = new Map([...GENERATED_ENDPOINT_FACTORIES].map((factory) => [`${factory}Generic`, factory]));
|
|
12
|
+
const normalizeModuleTarget = (targetRaw) => targetRaw.replace(/\\/g, '/').replace(/\.(?:[cm]?[jt]s)$/, '');
|
|
13
|
+
const isGeneratedServerModule = (targetRaw) => /(?:^|\/)_generated\/server$/.test(normalizeModuleTarget(targetRaw));
|
|
14
|
+
function importedConvexFactory(imports, localName) {
|
|
15
|
+
for (const parsedImport of imports) {
|
|
16
|
+
if (parsedImport.kind !== 'named' && parsedImport.kind !== 'alias')
|
|
17
|
+
continue;
|
|
18
|
+
if (parsedImport.localName !== localName)
|
|
19
|
+
continue;
|
|
20
|
+
const target = normalizeModuleTarget(parsedImport.targetRaw);
|
|
21
|
+
if (target === 'convex/server') {
|
|
22
|
+
return GENERIC_ENDPOINT_FACTORIES.get(parsedImport.importedName);
|
|
23
|
+
}
|
|
24
|
+
if (isGeneratedServerModule(target)) {
|
|
25
|
+
return GENERATED_ENDPOINT_FACTORIES.has(parsedImport.importedName)
|
|
26
|
+
? parsedImport.importedName
|
|
27
|
+
: undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
function matchingDeclarator(node, nodeName) {
|
|
33
|
+
if (node.type === 'variable_declarator' && node.childForFieldName('name')?.text === nodeName) {
|
|
34
|
+
return node;
|
|
35
|
+
}
|
|
36
|
+
if (node.type === 'export_statement') {
|
|
37
|
+
const declaration = node.childForFieldName('declaration');
|
|
38
|
+
return declaration ? matchingDeclarator(declaration, nodeName) : undefined;
|
|
39
|
+
}
|
|
40
|
+
if (node.type !== 'lexical_declaration' && node.type !== 'variable_declaration') {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
for (let i = 0; i < node.namedChildCount; i++) {
|
|
44
|
+
const child = node.namedChild(i);
|
|
45
|
+
if (child?.type === 'variable_declarator' &&
|
|
46
|
+
child.childForFieldName('name')?.text === nodeName) {
|
|
47
|
+
return child;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
function findDeclarator(node, nodeName) {
|
|
53
|
+
let current = node;
|
|
54
|
+
while (current) {
|
|
55
|
+
const declarator = matchingDeclarator(current, nodeName);
|
|
56
|
+
if (declarator)
|
|
57
|
+
return declarator;
|
|
58
|
+
if (current.type === 'program' || current.type === 'statement_block')
|
|
59
|
+
break;
|
|
60
|
+
current = current.parent;
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Stamp Convex runtime-dispatch metadata only when both the declaration shape
|
|
66
|
+
* and the factory import provenance are known. The MCP layer consumes the
|
|
67
|
+
* resulting property without reparsing lossy FTS text.
|
|
68
|
+
*/
|
|
69
|
+
export function extractConvexEndpointProperties(context) {
|
|
70
|
+
if ((context.nodeLabel !== 'Const' && context.nodeLabel !== 'Function') || !context.isExported) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const declarator = findDeclarator(context.definitionNode, context.nodeName);
|
|
74
|
+
const value = declarator?.childForFieldName('value');
|
|
75
|
+
if (!value || value.type !== 'call_expression')
|
|
76
|
+
return undefined;
|
|
77
|
+
const callee = value.childForFieldName('function');
|
|
78
|
+
if (!callee || callee.type !== 'identifier')
|
|
79
|
+
return undefined;
|
|
80
|
+
const factory = importedConvexFactory(context.parsedImports, callee.text);
|
|
81
|
+
if (factory === undefined)
|
|
82
|
+
return undefined;
|
|
83
|
+
const args = value.childForFieldName('arguments');
|
|
84
|
+
if (!args || args.namedChildCount !== 1)
|
|
85
|
+
return undefined;
|
|
86
|
+
const endpointDefinition = args.namedChild(0);
|
|
87
|
+
if (!endpointDefinition ||
|
|
88
|
+
!['object', 'arrow_function', 'function_expression'].includes(endpointDefinition.type)) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
return assertCloneable({ convexEndpointFactory: factory });
|
|
92
|
+
}
|
|
@@ -74,6 +74,7 @@ import { emitTsScopeCaptures, interpretTsImport, interpretTsTypeBinding, tsBindi
|
|
|
74
74
|
import { emitJsScopeCaptures, interpretJsImport, interpretJsTypeBinding, jsBindingScopeFor, jsImportOwningScope, jsReceiverBinding, jsMergeBindings, jsArityCompatibility, } from './javascript/index.js';
|
|
75
75
|
import { extractDispatchGuardRoutes } from '../route-extractors/dispatch-guard.js';
|
|
76
76
|
import { extractDataRouteTableRoutes } from '../route-extractors/data-route-table.js';
|
|
77
|
+
import { extractConvexEndpointProperties } from './typescript/convex-endpoint-metadata.js';
|
|
77
78
|
const extractJsTsRoutes = (...args) => [
|
|
78
79
|
...extractDispatchGuardRoutes(...args),
|
|
79
80
|
...extractDataRouteTableRoutes(...args),
|
|
@@ -347,6 +348,7 @@ export const typescriptProvider = defineLanguage({
|
|
|
347
348
|
extractFunctionName: tsExtractFunctionName,
|
|
348
349
|
}),
|
|
349
350
|
variableExtractor: createVariableExtractor(typescriptVariableConfig),
|
|
351
|
+
definitionPropertiesExtractor: extractConvexEndpointProperties,
|
|
350
352
|
classExtractor: createClassExtractor(typescriptClassConfig),
|
|
351
353
|
// ── JSDoc → description (issue #2270). An exported decl is captured as the
|
|
352
354
|
// inner declaration; its JSDoc precedes the wrapping `export_statement`. ──
|
|
@@ -430,6 +432,7 @@ export const javascriptProvider = defineLanguage({
|
|
|
430
432
|
extractFunctionName: tsExtractFunctionName,
|
|
431
433
|
}),
|
|
432
434
|
variableExtractor: createVariableExtractor(javascriptVariableConfig),
|
|
435
|
+
definitionPropertiesExtractor: extractConvexEndpointProperties,
|
|
433
436
|
classExtractor: createClassExtractor(javascriptClassConfig),
|
|
434
437
|
// ── JSDoc → description (issue #2270). An exported decl is captured as the
|
|
435
438
|
// inner declaration; its JSDoc precedes the wrapping `export_statement`. ──
|
|
@@ -69,7 +69,7 @@ import { defaultExportNameCollides } from '../languages/typescript/cjs-export-as
|
|
|
69
69
|
import { extractVueScript, extractTemplateComponents, isVueSetupTopLevel, } from '../vue-sfc-extractor.js';
|
|
70
70
|
import { buildMethodProps, arityForIdFromInfo, typeTagForId, constTagForId, buildCollisionGroups, parameterShapeIdTag, methodInfoKey, } from '../utils/method-props.js';
|
|
71
71
|
import { extractTemplateArguments, templateArgumentsIdTag, templateConstraintsIdTag, } from '../utils/template-arguments.js';
|
|
72
|
-
import { shouldHarvestModuleConstants } from '../language-provider.js';
|
|
72
|
+
import { mergeCanonicalDefinitionProperties, runDefinitionPropertiesExtractor, shouldHarvestModuleConstants, } from '../language-provider.js';
|
|
73
73
|
import { extractParsedFile } from '../scope-extractor-bridge.js';
|
|
74
74
|
import { persistParsedFileShardSync, persistDurableParsedFileShardSync, } from '../../../storage/parsedfile-store.js';
|
|
75
75
|
import { extractLaravelRoutes } from '../route-extractors/laravel.js';
|
|
@@ -2112,18 +2112,30 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
2112
2112
|
methodProps.scope = varInfo.scope;
|
|
2113
2113
|
}
|
|
2114
2114
|
}
|
|
2115
|
+
const isExported = language === SupportedLanguages.Vue && isVueSetup
|
|
2116
|
+
? isVueSetupTopLevel(nameNode || definitionNode)
|
|
2117
|
+
: cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName);
|
|
2118
|
+
if (definitionNode && provider.definitionPropertiesExtractor) {
|
|
2119
|
+
const definitionProperties = runDefinitionPropertiesExtractor(provider.definitionPropertiesExtractor, {
|
|
2120
|
+
nodeLabel,
|
|
2121
|
+
nodeName,
|
|
2122
|
+
definitionNode,
|
|
2123
|
+
parsedImports: parsedFile?.parsedImports ?? [],
|
|
2124
|
+
isExported,
|
|
2125
|
+
}, (error) => reportWarning(`Definition property extraction failed for ${file.path}:${nodeName}: ${error instanceof Error ? error.message : String(error)}`));
|
|
2126
|
+
if (definitionProperties !== undefined)
|
|
2127
|
+
Object.assign(methodProps, definitionProperties);
|
|
2128
|
+
}
|
|
2115
2129
|
result.nodes.push({
|
|
2116
2130
|
id: nodeId,
|
|
2117
2131
|
label: nodeLabel,
|
|
2118
|
-
properties: {
|
|
2132
|
+
properties: mergeCanonicalDefinitionProperties(methodProps, {
|
|
2119
2133
|
name: nodeName,
|
|
2120
2134
|
filePath: file.path,
|
|
2121
2135
|
startLine,
|
|
2122
2136
|
endLine: definitionNode ? definitionNode.endPosition.row + lineOffset : startLine,
|
|
2123
2137
|
language: language,
|
|
2124
|
-
isExported
|
|
2125
|
-
? isVueSetupTopLevel(nameNode || definitionNode)
|
|
2126
|
-
: cachedExportCheck(provider.exportChecker, nameNode || definitionNode, nodeName),
|
|
2138
|
+
isExported,
|
|
2127
2139
|
...(qualifiedTypeName !== undefined ? { qualifiedName: qualifiedTypeName } : {}),
|
|
2128
2140
|
...(classTemplateArguments !== undefined && classTemplateArguments.length > 0
|
|
2129
2141
|
? { templateArguments: classTemplateArguments }
|
|
@@ -2138,10 +2150,9 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
|
|
|
2138
2150
|
}
|
|
2139
2151
|
: {}),
|
|
2140
2152
|
...(description !== undefined ? { description } : {}),
|
|
2141
|
-
...methodProps,
|
|
2142
2153
|
...(declaredType !== undefined ? { declaredType } : {}),
|
|
2143
2154
|
...(returnShapeProperty ? { fromReturnShape: true, isDetail: true } : {}),
|
|
2144
|
-
},
|
|
2155
|
+
}),
|
|
2145
2156
|
});
|
|
2146
2157
|
// enclosingClassId already computed above (before nodeId generation)
|
|
2147
2158
|
const ownerId = enclosingClassId ?? objectLiteralOwnerInfo?.ownerId;
|
|
@@ -417,7 +417,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, onNodePhaseCo
|
|
|
417
417
|
const fileWriter = new BufferedCSVWriter(path.join(csvDir, 'file.csv'), 'id,name,filePath,content');
|
|
418
418
|
const folderWriter = new BufferedCSVWriter(path.join(csvDir, 'folder.csv'), 'id,name,filePath');
|
|
419
419
|
const codeElementHeader = 'id,name,filePath,startLine,endLine,isExported,content,description';
|
|
420
|
-
const functionWriter = new BufferedCSVWriter(path.join(csvDir, 'function.csv'), codeElementHeader);
|
|
420
|
+
const functionWriter = new BufferedCSVWriter(path.join(csvDir, 'function.csv'), `${codeElementHeader},convexEndpointFactory`);
|
|
421
421
|
const classWriter = new BufferedCSVWriter(path.join(csvDir, 'class.csv'), `${codeElementHeader},frameworkAnnotations`);
|
|
422
422
|
const interfaceWriter = new BufferedCSVWriter(path.join(csvDir, 'interface.csv'), codeElementHeader);
|
|
423
423
|
const methodHeader = 'id,name,filePath,startLine,endLine,isExported,content,description,parameterCount,returnType';
|
|
@@ -436,6 +436,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, onNodePhaseCo
|
|
|
436
436
|
const basicBlockWriter = new BufferedCSVWriter(path.join(csvDir, 'basicblock.csv'), BASICBLOCK_CSV_HEADER);
|
|
437
437
|
// Multi-language node types share the same CSV shape (no isExported column)
|
|
438
438
|
const multiLangHeader = 'id,name,filePath,startLine,endLine,content,description';
|
|
439
|
+
const constHeader = `${multiLangHeader},convexEndpointFactory`;
|
|
439
440
|
const MULTI_LANG_TYPES = [
|
|
440
441
|
'Struct',
|
|
441
442
|
'Enum',
|
|
@@ -460,7 +461,7 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, onNodePhaseCo
|
|
|
460
461
|
const propertyHeader = 'id,name,filePath,startLine,endLine,content,description,declaredType,isDetail';
|
|
461
462
|
const multiLangWriters = new Map();
|
|
462
463
|
for (const t of MULTI_LANG_TYPES) {
|
|
463
|
-
multiLangWriters.set(t, new BufferedCSVWriter(path.join(csvDir, `${t.toLowerCase()}.csv`), t === 'Property' ? propertyHeader : multiLangHeader));
|
|
464
|
+
multiLangWriters.set(t, new BufferedCSVWriter(path.join(csvDir, `${t.toLowerCase()}.csv`), t === 'Property' ? propertyHeader : t === 'Const' ? constHeader : multiLangHeader));
|
|
464
465
|
}
|
|
465
466
|
const codeWriterMap = {
|
|
466
467
|
Function: functionWriter,
|
|
@@ -609,6 +610,9 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, onNodePhaseCo
|
|
|
609
610
|
if (node.label === 'Class') {
|
|
610
611
|
row.push(escapeCSVField(formatCSVStringArray(node.properties.frameworkAnnotations)));
|
|
611
612
|
}
|
|
613
|
+
else if (node.label === 'Function') {
|
|
614
|
+
row.push(escapeCSVField(String(node.properties.convexEndpointFactory ?? '')));
|
|
615
|
+
}
|
|
612
616
|
pending = writer.addRow(row.join(','));
|
|
613
617
|
}
|
|
614
618
|
else {
|
|
@@ -632,7 +636,9 @@ export const streamAllCSVsToDisk = async (graph, repoPath, csvDir, onNodePhaseCo
|
|
|
632
636
|
// empty BOOLEAN cell fails the COPY.
|
|
633
637
|
node.properties.isDetail === true ? 'true' : 'false',
|
|
634
638
|
]
|
|
635
|
-
:
|
|
639
|
+
: node.label === 'Const'
|
|
640
|
+
? [escapeCSVField(String(node.properties.convexEndpointFactory ?? ''))]
|
|
641
|
+
: []),
|
|
636
642
|
].join(','));
|
|
637
643
|
}
|
|
638
644
|
else {
|
|
@@ -1336,9 +1336,15 @@ export const getCopyQuery = (table, filePath) => {
|
|
|
1336
1336
|
if (table === 'Method') {
|
|
1337
1337
|
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1338
1338
|
}
|
|
1339
|
+
if (table === 'Function') {
|
|
1340
|
+
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, convexEndpointFactory) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1341
|
+
}
|
|
1339
1342
|
if (table === 'Property') {
|
|
1340
1343
|
return `COPY ${t}(id, name, filePath, startLine, endLine, content, description, declaredType, isDetail) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1341
1344
|
}
|
|
1345
|
+
if (table === 'Const') {
|
|
1346
|
+
return `COPY ${t}(id, name, filePath, startLine, endLine, content, description, convexEndpointFactory) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
1347
|
+
}
|
|
1342
1348
|
// TypeScript/JS code element tables have isExported; multi-language tables do not
|
|
1343
1349
|
if (TABLES_WITH_EXPORTED.has(table)) {
|
|
1344
1350
|
return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description) FROM "${filePath}" ${COPY_CSV_OPTS}`;
|
|
@@ -1388,6 +1394,18 @@ export const insertNodeToLbug = async (label, properties, dbPath) => {
|
|
|
1388
1394
|
: '';
|
|
1389
1395
|
query = `CREATE (n:Class {id: ${formatCypherValue(properties.id)}, name: ${formatCypherValue(properties.name)}, filePath: ${formatCypherValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, isExported: ${!!properties.isExported}, content: ${formatCypherValue(properties.content || '')}${descPart}, frameworkAnnotations: ${formatCypherStringArray(properties.frameworkAnnotations)}})`;
|
|
1390
1396
|
}
|
|
1397
|
+
else if (label === 'Function') {
|
|
1398
|
+
const descPart = properties.description
|
|
1399
|
+
? `, description: ${formatCypherValue(properties.description)}`
|
|
1400
|
+
: '';
|
|
1401
|
+
query = `CREATE (n:Function {id: ${formatCypherValue(properties.id)}, name: ${formatCypherValue(properties.name)}, filePath: ${formatCypherValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, isExported: ${!!properties.isExported}, content: ${formatCypherValue(properties.content || '')}${descPart}, convexEndpointFactory: ${formatCypherValue(properties.convexEndpointFactory ?? '')}})`;
|
|
1402
|
+
}
|
|
1403
|
+
else if (label === 'Const') {
|
|
1404
|
+
const descPart = properties.description
|
|
1405
|
+
? `, description: ${formatCypherValue(properties.description)}`
|
|
1406
|
+
: '';
|
|
1407
|
+
query = `CREATE (n:Const {id: ${formatCypherValue(properties.id)}, name: ${formatCypherValue(properties.name)}, filePath: ${formatCypherValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, content: ${formatCypherValue(properties.content || '')}${descPart}, convexEndpointFactory: ${formatCypherValue(properties.convexEndpointFactory ?? '')}})`;
|
|
1408
|
+
}
|
|
1391
1409
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
1392
1410
|
const descPart = properties.description
|
|
1393
1411
|
? `, description: ${formatCypherValue(properties.description)}`
|
|
@@ -1478,6 +1496,18 @@ export const batchInsertNodesToLbug = async (nodes, dbPath) => {
|
|
|
1478
1496
|
: '';
|
|
1479
1497
|
query = `MERGE (n:Class {id: ${formatCypherValue(properties.id)}}) SET n.name = ${formatCypherValue(properties.name)}, n.filePath = ${formatCypherValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.isExported = ${!!properties.isExported}, n.content = ${formatCypherValue(properties.content || '')}${descPart}, n.frameworkAnnotations = ${formatCypherStringArray(properties.frameworkAnnotations)}`;
|
|
1480
1498
|
}
|
|
1499
|
+
else if (label === 'Function') {
|
|
1500
|
+
const descPart = properties.description
|
|
1501
|
+
? `, n.description = ${formatCypherValue(properties.description)}`
|
|
1502
|
+
: '';
|
|
1503
|
+
query = `MERGE (n:Function {id: ${formatCypherValue(properties.id)}}) SET n.name = ${formatCypherValue(properties.name)}, n.filePath = ${formatCypherValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.isExported = ${!!properties.isExported}, n.content = ${formatCypherValue(properties.content || '')}${descPart}, n.convexEndpointFactory = ${formatCypherValue(properties.convexEndpointFactory ?? '')}`;
|
|
1504
|
+
}
|
|
1505
|
+
else if (label === 'Const') {
|
|
1506
|
+
const descPart = properties.description
|
|
1507
|
+
? `, n.description = ${formatCypherValue(properties.description)}`
|
|
1508
|
+
: '';
|
|
1509
|
+
query = `MERGE (n:Const {id: ${formatCypherValue(properties.id)}}) SET n.name = ${formatCypherValue(properties.name)}, n.filePath = ${formatCypherValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.content = ${formatCypherValue(properties.content || '')}${descPart}, n.convexEndpointFactory = ${formatCypherValue(properties.convexEndpointFactory ?? '')}`;
|
|
1510
|
+
}
|
|
1481
1511
|
else if (TABLES_WITH_EXPORTED.has(label)) {
|
|
1482
1512
|
const descPart = properties.description
|
|
1483
1513
|
? `, n.description = ${formatCypherValue(properties.description)}`
|
|
@@ -13,7 +13,7 @@ export { NODE_TABLES, REL_TABLE_NAME, REL_TYPES, EMBEDDING_TABLE_NAME };
|
|
|
13
13
|
export type { NodeTableName, RelType } from '../../_shared/index.js';
|
|
14
14
|
export declare const FILE_SCHEMA = "\nCREATE NODE TABLE File (\n id STRING,\n name STRING,\n filePath STRING,\n content STRING,\n PRIMARY KEY (id)\n)";
|
|
15
15
|
export declare const FOLDER_SCHEMA = "\nCREATE NODE TABLE Folder (\n id STRING,\n name STRING,\n filePath STRING,\n PRIMARY KEY (id)\n)";
|
|
16
|
-
export declare const FUNCTION_SCHEMA = "\nCREATE NODE TABLE Function (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n isExported BOOLEAN,\n content STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
|
|
16
|
+
export declare const FUNCTION_SCHEMA = "\nCREATE NODE TABLE Function (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n isExported BOOLEAN,\n content STRING,\n description STRING,\n convexEndpointFactory STRING,\n PRIMARY KEY (id)\n)";
|
|
17
17
|
export declare const CLASS_SCHEMA = "\nCREATE NODE TABLE Class (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n isExported BOOLEAN,\n content STRING,\n description STRING,\n frameworkAnnotations STRING[],\n PRIMARY KEY (id)\n)";
|
|
18
18
|
export declare const INTERFACE_SCHEMA = "\nCREATE NODE TABLE Interface (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n isExported BOOLEAN,\n content STRING,\n description STRING,\n PRIMARY KEY (id)\n)";
|
|
19
19
|
export declare const METHOD_SCHEMA = "\nCREATE NODE TABLE Method (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n isExported BOOLEAN,\n content STRING,\n description STRING,\n parameterCount INT32,\n returnType STRING,\n PRIMARY KEY (id)\n)";
|
|
@@ -29,7 +29,7 @@ export declare const NAMESPACE_SCHEMA: string;
|
|
|
29
29
|
export declare const TRAIT_SCHEMA: string;
|
|
30
30
|
export declare const IMPL_SCHEMA: string;
|
|
31
31
|
export declare const TYPE_ALIAS_SCHEMA: string;
|
|
32
|
-
export declare const CONST_SCHEMA
|
|
32
|
+
export declare const CONST_SCHEMA = "\nCREATE NODE TABLE `Const` (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n content STRING,\n description STRING,\n convexEndpointFactory STRING,\n PRIMARY KEY (id)\n)";
|
|
33
33
|
export declare const STATIC_SCHEMA: string;
|
|
34
34
|
export declare const VARIABLE_SCHEMA: string;
|
|
35
35
|
export declare const PROPERTY_SCHEMA = "\nCREATE NODE TABLE `Property` (\n id STRING,\n name STRING,\n filePath STRING,\n startLine INT64,\n endLine INT64,\n content STRING,\n description STRING,\n declaredType STRING,\n /*\n * DETAIL SYMBOL \u2014 true when this property is a member of a shape that has no\n * independent identity: the keys of an anonymous literal returned from a\n * function (R3-4).\n *\n * It exists because indexing those keys is right for the GRAPH and wrong for\n * TEXT SEARCH. They are ordinary words (message, value, timestamp) and\n * there are many of them, so letting them into the FTS result set pushes the\n * CALLABLES named after the same concept past the row cap the search applies\n * \u2014 measured: query('message') went from two processes to none on the\n * mini-repo fixture. A ranking tweak cannot fix that, because the rows never\n * come back from the FTS call in the first place.\n *\n * So the search layer gained a notion it did not have \u2014 a symbol that is\n * queryable, walkable and impact-analysable, but not a concept a text search\n * should surface on its own. buildFtsQueryCypher excludes these for the\n * Property table only; every other consumer sees them normally.\n */\n isDetail BOOLEAN,\n PRIMARY KEY (id)\n)";
|
package/dist/core/lbug/schema.js
CHANGED
|
@@ -44,6 +44,7 @@ CREATE NODE TABLE Function (
|
|
|
44
44
|
isExported BOOLEAN,
|
|
45
45
|
content STRING,
|
|
46
46
|
description STRING,
|
|
47
|
+
convexEndpointFactory STRING,
|
|
47
48
|
PRIMARY KEY (id)
|
|
48
49
|
)`;
|
|
49
50
|
export const CLASS_SCHEMA = `
|
|
@@ -152,7 +153,18 @@ export const NAMESPACE_SCHEMA = CODE_ELEMENT_BASE('Namespace');
|
|
|
152
153
|
export const TRAIT_SCHEMA = CODE_ELEMENT_BASE('Trait');
|
|
153
154
|
export const IMPL_SCHEMA = CODE_ELEMENT_BASE('Impl');
|
|
154
155
|
export const TYPE_ALIAS_SCHEMA = CODE_ELEMENT_BASE('TypeAlias');
|
|
155
|
-
export const CONST_SCHEMA =
|
|
156
|
+
export const CONST_SCHEMA = `
|
|
157
|
+
CREATE NODE TABLE \`Const\` (
|
|
158
|
+
id STRING,
|
|
159
|
+
name STRING,
|
|
160
|
+
filePath STRING,
|
|
161
|
+
startLine INT64,
|
|
162
|
+
endLine INT64,
|
|
163
|
+
content STRING,
|
|
164
|
+
description STRING,
|
|
165
|
+
convexEndpointFactory STRING,
|
|
166
|
+
PRIMARY KEY (id)
|
|
167
|
+
)`;
|
|
156
168
|
export const STATIC_SCHEMA = CODE_ELEMENT_BASE('Static');
|
|
157
169
|
export const VARIABLE_SCHEMA = CODE_ELEMENT_BASE('Variable');
|
|
158
170
|
export const PROPERTY_SCHEMA = `
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { executeParameterized } from '../../core/lbug/pool-adapter.js';
|
|
2
|
+
export interface ConvexDispatchMetadata {
|
|
3
|
+
readonly factory?: string;
|
|
4
|
+
readonly boundary: string;
|
|
5
|
+
readonly staleIndex?: true;
|
|
6
|
+
readonly probeFailed?: true;
|
|
7
|
+
}
|
|
8
|
+
export declare function queryConvexDispatchMetadata(lbugPath: string, symbolId: string, symbolName: string, symbolType: string, runQuery?: typeof executeParameterized): Promise<ConvexDispatchMetadata | undefined>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { executeParameterized } from '../../core/lbug/pool-adapter.js';
|
|
2
|
+
import { logger } from '../../core/logger.js';
|
|
3
|
+
function isMissingConvexMetadataProperty(error) {
|
|
4
|
+
const message = error instanceof Error ? error.message : String(error ?? '');
|
|
5
|
+
return /cannot find property\s+convexEndpointFactory|property[^\n]*convexEndpointFactory[^\n]*not (?:defined|found)/i.test(message);
|
|
6
|
+
}
|
|
7
|
+
export async function queryConvexDispatchMetadata(lbugPath, symbolId, symbolName, symbolType, runQuery = executeParameterized) {
|
|
8
|
+
if (symbolType !== 'Const' && symbolType !== 'Function')
|
|
9
|
+
return undefined;
|
|
10
|
+
const nodeLabel = symbolType === 'Function' ? 'Function' : 'Const';
|
|
11
|
+
try {
|
|
12
|
+
const rows = await runQuery(lbugPath, `MATCH (n:${nodeLabel} {id: $symbolId})
|
|
13
|
+
RETURN n.convexEndpointFactory AS factory`, { symbolId });
|
|
14
|
+
const row = rows[0];
|
|
15
|
+
if (row === undefined)
|
|
16
|
+
return undefined;
|
|
17
|
+
const factory = String(row.factory ?? row[0] ?? '');
|
|
18
|
+
if (factory.length === 0)
|
|
19
|
+
return undefined;
|
|
20
|
+
return {
|
|
21
|
+
factory,
|
|
22
|
+
boundary: `${symbolName} is exported through Convex ${factory}({...}) and can be addressed through ` +
|
|
23
|
+
`the anyApi runtime proxy; callers across that dynamic-dispatch boundary leave no static ` +
|
|
24
|
+
`edge, so actual impact may be higher.`,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (isMissingConvexMetadataProperty(error)) {
|
|
29
|
+
return {
|
|
30
|
+
staleIndex: true,
|
|
31
|
+
boundary: 'Convex runtime-proxy metadata is unavailable because this index predates ' +
|
|
32
|
+
'convexEndpointFactory; re-index before treating impact as exact.',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
logger.warn({
|
|
36
|
+
context: 'impact:convex-metadata',
|
|
37
|
+
err: error instanceof Error ? error.message : String(error),
|
|
38
|
+
}, 'GitNexus Convex metadata probe failed (degraded)');
|
|
39
|
+
return {
|
|
40
|
+
probeFailed: true,
|
|
41
|
+
boundary: 'Convex runtime-proxy metadata could not be checked; impact remains a lower bound ' +
|
|
42
|
+
'until the metadata probe succeeds.',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -94,9 +94,8 @@ export interface EpistemicCauses {
|
|
|
94
94
|
*/
|
|
95
95
|
readonly receiverTyping: number;
|
|
96
96
|
/**
|
|
97
|
-
* Symbols on
|
|
98
|
-
* attribute
|
|
99
|
-
* consumers, summed over the boundary nodes that were flagged.
|
|
97
|
+
* Symbols on or beyond a dispatch boundary that the traversal could not
|
|
98
|
+
* attribute statically: implementations plus interface-level consumers.
|
|
100
99
|
*
|
|
101
100
|
* Unit: SYMBOLS, not call sites — deliberately, because a call-site count is
|
|
102
101
|
* not derivable on this side. The graph does not retain per-site multiplicity
|
|
@@ -106,6 +105,10 @@ export interface EpistemicCauses {
|
|
|
106
105
|
* symbol reachable through two flagged boundary nodes is counted once per
|
|
107
106
|
* node, so this is itself a lower bound.
|
|
108
107
|
*
|
|
108
|
+
* Framework runtime-proxy metadata can prove that impact is incomplete but
|
|
109
|
+
* cannot provide this magnitude, so it contributes a boundary note while
|
|
110
|
+
* leaving this count unchanged.
|
|
111
|
+
*
|
|
109
112
|
* It is still directly comparable in magnitude with `receiverTyping` — both
|
|
110
113
|
* answer "how much is missing" — which `boundaries.length` was not.
|
|
111
114
|
*/
|
|
@@ -11,6 +11,7 @@ import { createHash } from 'crypto';
|
|
|
11
11
|
import { initLbug, executeQuery, executeParameterized, closeLbug, isLbugReady, statDbIdentity, dbIdentityChanged, } from '../../core/lbug/pool-adapter.js';
|
|
12
12
|
import { queryClassBeanMetadata } from './bean-metadata.js';
|
|
13
13
|
import { querySpringAopMetadata } from './aop-metadata.js';
|
|
14
|
+
import { queryConvexDispatchMetadata } from './convex-metadata.js';
|
|
14
15
|
import { isValidQueryParams } from '../../core/lbug/query-params.js';
|
|
15
16
|
import { toDisplayLine } from './line-display.js';
|
|
16
17
|
import { LBUG_ID_PROBE_BATCH_SIZE, LBUG_QUERY_BATCH_SIZE } from '../../core/lbug/query-batch.js';
|
|
@@ -464,7 +465,7 @@ function epistemicFrom(dropped) {
|
|
|
464
465
|
epistemic: 'exact',
|
|
465
466
|
causes: {
|
|
466
467
|
receiverTyping: 0,
|
|
467
|
-
dispatchBoundary:
|
|
468
|
+
dispatchBoundary: dropped.dispatch,
|
|
468
469
|
externalBoundary: dropped.external,
|
|
469
470
|
undecidedSatisfaction: 0,
|
|
470
471
|
},
|
|
@@ -479,7 +480,7 @@ function epistemicFrom(dropped) {
|
|
|
479
480
|
// would read a different magnitude than the human reading the text.
|
|
480
481
|
causes: {
|
|
481
482
|
receiverTyping: dropped.sites,
|
|
482
|
-
dispatchBoundary:
|
|
483
|
+
dispatchBoundary: dropped.dispatch,
|
|
483
484
|
externalBoundary: dropped.external,
|
|
484
485
|
undecidedSatisfaction: dropped.undecided,
|
|
485
486
|
},
|
|
@@ -5524,7 +5525,7 @@ export class LocalBackend {
|
|
|
5524
5525
|
this.crossLanguagePropertyCache.set(repo.lbugPath, { indexedAt: repo.indexedAt, byName });
|
|
5525
5526
|
return byName;
|
|
5526
5527
|
}
|
|
5527
|
-
async computeEpistemicBoundary(repo, symId, symType, symName) {
|
|
5528
|
+
async computeEpistemicBoundary(repo, symId, symType, symName, direction) {
|
|
5528
5529
|
const HERITAGE_TYPES = EPISTEMIC_HERITAGE_RELATION_TYPES;
|
|
5529
5530
|
const CONSUMER_TYPES = EPISTEMIC_CONSUMER_RELATION_TYPES;
|
|
5530
5531
|
// #2744 — call sites dropped for want of a receiver type. Checked BEFORE
|
|
@@ -5557,6 +5558,14 @@ export class LocalBackend {
|
|
|
5557
5558
|
// the owning-type hop below would be a graph round-trip per method query in
|
|
5558
5559
|
// every index that has no such record — which is every non-Go one, since Go
|
|
5559
5560
|
// is the only language with a structural-satisfaction hook.
|
|
5561
|
+
const convexDispatchPromise = direction === 'downstream'
|
|
5562
|
+
? Promise.resolve(undefined)
|
|
5563
|
+
: queryConvexDispatchMetadata(repo.lbugPath, symId, symName, symType);
|
|
5564
|
+
const interfaceRowsPromise = executeParameterized(repo.lbugPath, `MATCH (x)-[r:CodeRelation]->(iface)
|
|
5565
|
+
WHERE x.id = $symId AND r.type IN $heritage
|
|
5566
|
+
RETURN DISTINCT iface.id AS id, iface.name AS name, labels(iface)[0] AS label
|
|
5567
|
+
ORDER BY id
|
|
5568
|
+
LIMIT 25`, { symId, heritage: HERITAGE_TYPES }).catch(() => []);
|
|
5560
5569
|
const undecidedSummary = meta?.undecidedInterfaceSatisfaction;
|
|
5561
5570
|
const undecidedDrops = undecidedSummary === undefined
|
|
5562
5571
|
? { notes: [], undecided: 0 }
|
|
@@ -5566,10 +5575,19 @@ export class LocalBackend {
|
|
|
5566
5575
|
? await this.owningTypeNames(repo, symId)
|
|
5567
5576
|
: []),
|
|
5568
5577
|
]);
|
|
5578
|
+
const convexDispatch = await convexDispatchPromise;
|
|
5569
5579
|
const droppedBoundaries = {
|
|
5570
5580
|
...receiverDrops,
|
|
5571
|
-
notes: [
|
|
5581
|
+
notes: [
|
|
5582
|
+
...receiverDrops.notes,
|
|
5583
|
+
...undecidedDrops.notes,
|
|
5584
|
+
...(convexDispatch === undefined ? [] : [convexDispatch.boundary]),
|
|
5585
|
+
],
|
|
5572
5586
|
undecided: undecidedDrops.undecided,
|
|
5587
|
+
// Endpoint/probe evidence proves incompleteness but does not expose a
|
|
5588
|
+
// count of omitted symbols. Keep the magnitude at zero rather than
|
|
5589
|
+
// inventing one from the presence of a note.
|
|
5590
|
+
dispatch: 0,
|
|
5573
5591
|
};
|
|
5574
5592
|
try {
|
|
5575
5593
|
// Discover the interface / abstract supertypes on the target's boundary.
|
|
@@ -5578,11 +5596,7 @@ export class LocalBackend {
|
|
|
5578
5596
|
if (symType === 'Interface') {
|
|
5579
5597
|
boundary.set(symId, { name: symName || '', label: 'Interface' });
|
|
5580
5598
|
}
|
|
5581
|
-
const ifaceRows = await
|
|
5582
|
-
WHERE x.id = $symId AND r.type IN $heritage
|
|
5583
|
-
RETURN DISTINCT iface.id AS id, iface.name AS name, labels(iface)[0] AS label
|
|
5584
|
-
ORDER BY id
|
|
5585
|
-
LIMIT 25`, { symId, heritage: HERITAGE_TYPES }).catch(() => []);
|
|
5599
|
+
const ifaceRows = await interfaceRowsPromise;
|
|
5586
5600
|
for (const r of ifaceRows) {
|
|
5587
5601
|
const id = (r.id ?? r[0]);
|
|
5588
5602
|
if (id && !boundary.has(id)) {
|
|
@@ -5650,7 +5664,7 @@ export class LocalBackend {
|
|
|
5650
5664
|
boundaries: [...droppedBoundaries.notes, ...boundaries],
|
|
5651
5665
|
causes: {
|
|
5652
5666
|
receiverTyping: droppedBoundaries.sites,
|
|
5653
|
-
dispatchBoundary: dispatchBoundarySymbols,
|
|
5667
|
+
dispatchBoundary: droppedBoundaries.dispatch + dispatchBoundarySymbols,
|
|
5654
5668
|
externalBoundary: droppedBoundaries.external,
|
|
5655
5669
|
undecidedSatisfaction: droppedBoundaries.undecided,
|
|
5656
5670
|
},
|
|
@@ -5713,7 +5727,7 @@ export class LocalBackend {
|
|
|
5713
5727
|
// so every consumer would be reading a field the compiler says is absent.
|
|
5714
5728
|
const epistemicPromise = opts.skipEpistemic
|
|
5715
5729
|
? Promise.resolve({})
|
|
5716
|
-
: this.computeEpistemicBoundary(repo, symId, symType, (sym.name || sym[1]));
|
|
5730
|
+
: this.computeEpistemicBoundary(repo, symId, symType, (sym.name || sym[1]), direction);
|
|
5717
5731
|
const beanMetadataPromise = opts.skipEpistemic || summaryOnly
|
|
5718
5732
|
? Promise.resolve(undefined)
|
|
5719
5733
|
: queryClassBeanMetadata(repo.lbugPath, symId, symType);
|
package/dist/mcp/tools.js
CHANGED
|
@@ -251,9 +251,10 @@ COMPLETENESS OF incoming: alongside symbol/incoming/outgoing the result carries
|
|
|
251
251
|
- causes: { receiverTyping, dispatchBoundary, externalBoundary, undecidedSatisfaction } — machine-readable WHY. Every field counts MISSING THINGS, never sentences:
|
|
252
252
|
- 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
253
|
- 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
|
-
- causes.dispatchBoundary (unit: symbols) > 0 — DI
|
|
254
|
+
- 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
|
+
- 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.
|
|
255
256
|
|
|
256
|
-
REQUIRES RE-INDEX: causes.receiverTyping
|
|
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.
|
|
257
258
|
|
|
258
259
|
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".
|
|
259
260
|
|
|
@@ -440,11 +441,11 @@ Output includes:
|
|
|
440
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:
|
|
441
442
|
- 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.
|
|
442
443
|
- 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.
|
|
443
|
-
- causes.dispatchBoundary (unit: symbols) > 0 — DI
|
|
444
|
+
- 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.
|
|
444
445
|
|
|
445
446
|
- 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.
|
|
446
447
|
|
|
447
|
-
REQUIRES RE-INDEX: causes.receiverTyping, causes.externalBoundary
|
|
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.
|
|
448
449
|
|
|
449
450
|
Depth groups:
|
|
450
451
|
- d=1: WILL BREAK (direct callers/importers)
|
|
@@ -564,7 +564,16 @@ import { fileURLToPath } from 'url';
|
|
|
564
564
|
// IN-FLIGHT claim, not above origin/main. Every open PR touching gitnexus/ was
|
|
565
565
|
// scanned; #3017 is the only other claimant.
|
|
566
566
|
// RE-CHECK AGAINST origin/main AND OPEN PRs IMMEDIATELY BEFORE MERGING.
|
|
567
|
-
|
|
567
|
+
//
|
|
568
|
+
// 72 -> 74 adds import-proven Convex endpoint metadata to Const/Function worker
|
|
569
|
+
// output. A warm v72 cache has no convexEndpointFactory property, so the MCP
|
|
570
|
+
// impact probe would keep claiming exact results for unchanged endpoints. The
|
|
571
|
+
// parse-cache bump makes unchanged files re-parse; analyzer runner identity
|
|
572
|
+
// drift separately forces the graph re-emit (run-analyze.ts), and an id/schema
|
|
573
|
+
// migration needs both guarantees. Version 73 is intentionally skipped because
|
|
574
|
+
// concurrent PR #3046 (fixes #3041) claims it. Re-check main and open PRs
|
|
575
|
+
// immediately before merge.
|
|
576
|
+
const SCHEMA_BUMP = 74;
|
|
568
577
|
const GITNEXUS_PKG_VERSION = (() => {
|
|
569
578
|
try {
|
|
570
579
|
// package.json sits at gitnexus/package.json — two levels up from
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gitnexus",
|
|
3
|
-
"version": "1.6.10-rc.
|
|
3
|
+
"version": "1.6.10-rc.220",
|
|
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",
|