gitnexus 1.6.3-rc.11 → 1.6.3-rc.13

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.
@@ -242,9 +242,15 @@ interface LanguageProviderConfig {
242
242
  * Providers that have not yet migrated continue to run through the
243
243
  * legacy DAG path (feature-flagged per `REGISTRY_PRIMARY_<LANG>`).
244
244
  *
245
+ * **Sync return.** Tree-sitter query execution and COBOL's regex
246
+ * tagger are both synchronous; no current or foreseeable provider
247
+ * needs async work inside this hook. The sync signature lets
248
+ * `parse-worker.ts` (#920) invoke it inline in its already-sync
249
+ * per-file loop without cascading `async` through the batch pipeline.
250
+ *
245
251
  * Default: undefined (language continues to use legacy DAG).
246
252
  */
247
- readonly emitScopeCaptures?: (sourceText: string, filePath: string) => Promise<readonly CaptureMatch[]>;
253
+ readonly emitScopeCaptures?: (sourceText: string, filePath: string) => readonly CaptureMatch[];
248
254
  /**
249
255
  * Interpret a raw `@import.statement` capture group into a `ParsedImport`.
250
256
  * The central finalize algorithm resolves `ParsedImport.targetRaw` to a
@@ -1,6 +1,7 @@
1
1
  import { KnowledgeGraph } from '../graph/types.js';
2
2
  import type { SymbolTableWriter, ExtractedHeritage } from './model/index.js';
3
3
  import { ASTCache } from './ast-cache.js';
4
+ import type { ParsedFile } from '../../_shared/index.js';
4
5
  import { WorkerPool } from './workers/worker-pool.js';
5
6
  import type { ExtractedImport, ExtractedCall, ExtractedAssignment, ExtractedRoute, ExtractedFetchCall, ExtractedDecoratorRoute, ExtractedToolDef, FileConstructorBindings, FileScopeBindings, ExtractedORMQuery } from './workers/parse-worker.js';
6
7
  export type FileProgressCallback = (current: number, total: number, filePath: string) => void;
@@ -16,6 +17,14 @@ export interface WorkerExtractedData {
16
17
  ormQueries: ExtractedORMQuery[];
17
18
  constructorBindings: FileConstructorBindings[];
18
19
  fileScopeBindings: FileScopeBindings[];
20
+ /**
21
+ * Per-file `ParsedFile` artifacts from the new scope-based resolution
22
+ * pipeline (RFC #909 Ring 2). Empty until a provider implements
23
+ * `emitScopeCaptures` — additive to the legacy DAG path. Aggregated
24
+ * from every worker chunk; consumed downstream by #921's
25
+ * finalize-orchestrator.
26
+ */
27
+ parsedFiles: ParsedFile[];
19
28
  }
20
29
  export declare const processParsing: (graph: KnowledgeGraph, files: {
21
30
  path: string;
@@ -34,6 +34,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
34
34
  ormQueries: [],
35
35
  constructorBindings: [],
36
36
  fileScopeBindings: [],
37
+ parsedFiles: [],
37
38
  };
38
39
  const total = files.length;
39
40
  // Dispatch to worker pool — pool handles splitting into chunks and sub-batching
@@ -52,6 +53,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
52
53
  const allORMQueries = [];
53
54
  const allConstructorBindings = [];
54
55
  const fileScopeBindingsByFile = [];
56
+ const allParsedFiles = [];
55
57
  for (const result of chunkResults) {
56
58
  for (const node of result.nodes) {
57
59
  graph.addNode({
@@ -98,6 +100,13 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
98
100
  if (result.fileScopeBindings)
99
101
  for (const item of result.fileScopeBindings)
100
102
  fileScopeBindingsByFile.push(item);
103
+ // RFC #909 Ring 2: aggregate per-file scope artifacts. Tolerant of
104
+ // workers that don't emit the field yet (older worker builds or
105
+ // partial rollouts), since the additive contract means undefined =
106
+ // "this worker produced no ParsedFiles for this chunk".
107
+ if (result.parsedFiles)
108
+ for (const item of result.parsedFiles)
109
+ allParsedFiles.push(item);
101
110
  }
102
111
  // Merge and log skipped languages from workers
103
112
  const skippedLanguages = new Map();
@@ -126,6 +135,7 @@ const processParsingWithWorkers = async (graph, files, symbolTable, astCache, wo
126
135
  ormQueries: allORMQueries,
127
136
  constructorBindings: allConstructorBindings,
128
137
  fileScopeBindings: fileScopeBindingsByFile,
138
+ parsedFiles: allParsedFiles,
129
139
  };
130
140
  };
131
141
  // ============================================================================
@@ -0,0 +1,59 @@
1
+ /**
2
+ * `REGISTRY_PRIMARY_<LANG>` per-language feature flags for the scope-based
3
+ * resolution rollout (RFC §6.1 Ring 3; Ring 2 PKG #924).
4
+ *
5
+ * This module is the single source of truth for whether a given language
6
+ * has been flipped to registry-primary call resolution. When a language's
7
+ * flag is true, its files route through `Registry.lookup` (RFC §4) instead
8
+ * of the legacy call-resolution DAG; when false (the default), the legacy
9
+ * DAG runs unchanged.
10
+ *
11
+ * ## Contract
12
+ *
13
+ * - Env-var name per language: `REGISTRY_PRIMARY_<UPPER(enum-value)>`.
14
+ * Example: `SupportedLanguages.Python` → `REGISTRY_PRIMARY_PYTHON`;
15
+ * `SupportedLanguages.CPlusPlus` (value `'cpp'`) → `REGISTRY_PRIMARY_CPP`.
16
+ * - Truthy values: `'true'`, `'1'`, `'yes'` (case-insensitive,
17
+ * whitespace-trimmed). Anything else — including `undefined`, empty
18
+ * string, or unknown tokens — is `false`.
19
+ * - No per-process caching. `process.env` is read on every call. The
20
+ * flag is consulted once per file at call-resolution time, so the
21
+ * overhead is negligible; skipping caching keeps test isolation
22
+ * trivial (no `resetFlagCache()` coordination needed).
23
+ *
24
+ * ## Integration site
25
+ *
26
+ * `call-processor.ts` integration lands in **#921** (`finalize-orchestrator`)
27
+ * where the `SemanticModel` becomes accessible and `Registry.lookup` can
28
+ * actually be called with a populated context. This module ships the flag
29
+ * primitive in isolation so #921 has a clean, tested utility to consult.
30
+ *
31
+ * ## Shadow mode is orthogonal
32
+ *
33
+ * Shadow mode (`GITNEXUS_SHADOW_MODE=1`, introduced in #923) runs BOTH
34
+ * legacy and registry paths regardless of the per-language flag, so the
35
+ * parity dashboard has signal even for un-flipped languages. That logic
36
+ * lives in `shadow-harness.ts` (#923), not here.
37
+ */
38
+ import { SupportedLanguages } from '../../_shared/index.js';
39
+ /**
40
+ * Return the env-var name that controls a given language's registry-
41
+ * primary flag. Exported for test assertions and for the PR-labeling
42
+ * CI job that cross-references per-language flag changes.
43
+ */
44
+ export declare function envVarNameFor(lang: SupportedLanguages): string;
45
+ /**
46
+ * Whether `lang` has been flipped to registry-primary call resolution.
47
+ *
48
+ * Returns `false` by default — a language must explicitly set its env
49
+ * var to a truthy value to opt in. The flag is the sole control surface:
50
+ * flipping it requires no code change, and reverting it requires no code
51
+ * change.
52
+ */
53
+ export declare function isRegistryPrimary(lang: SupportedLanguages): boolean;
54
+ /**
55
+ * All languages whose registry-primary flag is currently on. Useful for
56
+ * startup-time logging + the shadow-harness dashboard, which wants to
57
+ * distinguish "primary: legacy" from "primary: registry" rows.
58
+ */
59
+ export declare function primaryLanguages(): ReadonlySet<SupportedLanguages>;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * `REGISTRY_PRIMARY_<LANG>` per-language feature flags for the scope-based
3
+ * resolution rollout (RFC §6.1 Ring 3; Ring 2 PKG #924).
4
+ *
5
+ * This module is the single source of truth for whether a given language
6
+ * has been flipped to registry-primary call resolution. When a language's
7
+ * flag is true, its files route through `Registry.lookup` (RFC §4) instead
8
+ * of the legacy call-resolution DAG; when false (the default), the legacy
9
+ * DAG runs unchanged.
10
+ *
11
+ * ## Contract
12
+ *
13
+ * - Env-var name per language: `REGISTRY_PRIMARY_<UPPER(enum-value)>`.
14
+ * Example: `SupportedLanguages.Python` → `REGISTRY_PRIMARY_PYTHON`;
15
+ * `SupportedLanguages.CPlusPlus` (value `'cpp'`) → `REGISTRY_PRIMARY_CPP`.
16
+ * - Truthy values: `'true'`, `'1'`, `'yes'` (case-insensitive,
17
+ * whitespace-trimmed). Anything else — including `undefined`, empty
18
+ * string, or unknown tokens — is `false`.
19
+ * - No per-process caching. `process.env` is read on every call. The
20
+ * flag is consulted once per file at call-resolution time, so the
21
+ * overhead is negligible; skipping caching keeps test isolation
22
+ * trivial (no `resetFlagCache()` coordination needed).
23
+ *
24
+ * ## Integration site
25
+ *
26
+ * `call-processor.ts` integration lands in **#921** (`finalize-orchestrator`)
27
+ * where the `SemanticModel` becomes accessible and `Registry.lookup` can
28
+ * actually be called with a populated context. This module ships the flag
29
+ * primitive in isolation so #921 has a clean, tested utility to consult.
30
+ *
31
+ * ## Shadow mode is orthogonal
32
+ *
33
+ * Shadow mode (`GITNEXUS_SHADOW_MODE=1`, introduced in #923) runs BOTH
34
+ * legacy and registry paths regardless of the per-language flag, so the
35
+ * parity dashboard has signal even for un-flipped languages. That logic
36
+ * lives in `shadow-harness.ts` (#923), not here.
37
+ */
38
+ import { SupportedLanguages } from '../../_shared/index.js';
39
+ /**
40
+ * Return the env-var name that controls a given language's registry-
41
+ * primary flag. Exported for test assertions and for the PR-labeling
42
+ * CI job that cross-references per-language flag changes.
43
+ */
44
+ export function envVarNameFor(lang) {
45
+ return `REGISTRY_PRIMARY_${lang.toUpperCase()}`;
46
+ }
47
+ /**
48
+ * Whether `lang` has been flipped to registry-primary call resolution.
49
+ *
50
+ * Returns `false` by default — a language must explicitly set its env
51
+ * var to a truthy value to opt in. The flag is the sole control surface:
52
+ * flipping it requires no code change, and reverting it requires no code
53
+ * change.
54
+ */
55
+ export function isRegistryPrimary(lang) {
56
+ return parseFlag(process.env[envVarNameFor(lang)]);
57
+ }
58
+ /**
59
+ * All languages whose registry-primary flag is currently on. Useful for
60
+ * startup-time logging + the shadow-harness dashboard, which wants to
61
+ * distinguish "primary: legacy" from "primary: registry" rows.
62
+ */
63
+ export function primaryLanguages() {
64
+ const out = new Set();
65
+ for (const lang of Object.values(SupportedLanguages)) {
66
+ if (isRegistryPrimary(lang))
67
+ out.add(lang);
68
+ }
69
+ return out;
70
+ }
71
+ // ─── Internal ───────────────────────────────────────────────────────────────
72
+ /** Accepted truthy strings (case-insensitive, trimmed). */
73
+ const TRUTHY_VALUES = new Set(['true', '1', 'yes']);
74
+ function parseFlag(raw) {
75
+ if (raw === undefined)
76
+ return false;
77
+ return TRUTHY_VALUES.has(raw.trim().toLowerCase());
78
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Bridge between a language provider's `emitScopeCaptures` hook and the
3
+ * `ScopeExtractor` (RFC #909 Ring 2 PKG #920).
4
+ *
5
+ * Extracted into its own module so it can be imported by test code
6
+ * without pulling in `parse-worker.ts` — which has a top-level
7
+ * `parentPort!.on('message', ...)` call that assumes a worker-thread
8
+ * context and throws on direct import.
9
+ *
10
+ * The bridge:
11
+ *
12
+ * 1. Short-circuits when the provider has NOT implemented
13
+ * `emitScopeCaptures`. Returns `undefined`; zero work done. This is
14
+ * the state of every language today — `ParsedFile` production stays
15
+ * dormant until a language migrates.
16
+ * 2. Invokes the hook + feeds its output to `ScopeExtractor.extract`.
17
+ * 3. **Swallows exceptions from either side.** A failure here returns
18
+ * `undefined` and emits a warning via `onWarn`; legacy parsing on
19
+ * the same file continues unaffected by the scope-extraction miss.
20
+ * Scope-based resolution is the new path under construction — it
21
+ * must not destabilize the legacy DAG.
22
+ */
23
+ import type { ParsedFile } from '../../_shared/index.js';
24
+ import type { LanguageProvider } from './language-provider.js';
25
+ /** Callback used to report scope-extraction warnings to the host (worker or direct). */
26
+ export type ScopeBridgeWarn = (message: string) => void;
27
+ /**
28
+ * Produce a `ParsedFile` for the given file, or `undefined` when the
29
+ * provider hasn't migrated / the extractor throws. Never propagates
30
+ * exceptions.
31
+ */
32
+ export declare function extractParsedFile(provider: LanguageProvider, sourceText: string, filePath: string, onWarn?: ScopeBridgeWarn): ParsedFile | undefined;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Bridge between a language provider's `emitScopeCaptures` hook and the
3
+ * `ScopeExtractor` (RFC #909 Ring 2 PKG #920).
4
+ *
5
+ * Extracted into its own module so it can be imported by test code
6
+ * without pulling in `parse-worker.ts` — which has a top-level
7
+ * `parentPort!.on('message', ...)` call that assumes a worker-thread
8
+ * context and throws on direct import.
9
+ *
10
+ * The bridge:
11
+ *
12
+ * 1. Short-circuits when the provider has NOT implemented
13
+ * `emitScopeCaptures`. Returns `undefined`; zero work done. This is
14
+ * the state of every language today — `ParsedFile` production stays
15
+ * dormant until a language migrates.
16
+ * 2. Invokes the hook + feeds its output to `ScopeExtractor.extract`.
17
+ * 3. **Swallows exceptions from either side.** A failure here returns
18
+ * `undefined` and emits a warning via `onWarn`; legacy parsing on
19
+ * the same file continues unaffected by the scope-extraction miss.
20
+ * Scope-based resolution is the new path under construction — it
21
+ * must not destabilize the legacy DAG.
22
+ */
23
+ import { extract as extractScope } from './scope-extractor.js';
24
+ /**
25
+ * Produce a `ParsedFile` for the given file, or `undefined` when the
26
+ * provider hasn't migrated / the extractor throws. Never propagates
27
+ * exceptions.
28
+ */
29
+ export function extractParsedFile(provider, sourceText, filePath, onWarn) {
30
+ if (provider.emitScopeCaptures === undefined)
31
+ return undefined;
32
+ try {
33
+ const captures = provider.emitScopeCaptures(sourceText, filePath);
34
+ return extractScope(captures, filePath, provider);
35
+ }
36
+ catch (err) {
37
+ const message = `scope extraction failed for ${filePath}: ${err instanceof Error ? err.message : String(err)}`;
38
+ if (onWarn !== undefined)
39
+ onWarn(message);
40
+ else
41
+ console.warn(message);
42
+ return undefined;
43
+ }
44
+ }
@@ -4,6 +4,7 @@ import { type MixedChainStep } from '../utils/call-analysis.js';
4
4
  import type { ConstructorBinding } from '../type-env.js';
5
5
  import type { NamedBinding } from '../named-bindings/types.js';
6
6
  import type { NodeLabel } from '../../../_shared/index.js';
7
+ import type { ParsedFile } from '../../../_shared/index.js';
7
8
  interface ParsedNode {
8
9
  id: string;
9
10
  label: string;
@@ -174,6 +175,14 @@ export interface ParseWorkerResult {
174
175
  constructorBindings: FileConstructorBindings[];
175
176
  /** All-scope type bindings from TypeEnv for BindingAccumulator (includes function-local). */
176
177
  fileScopeBindings: FileScopeBindings[];
178
+ /**
179
+ * Per-file `ParsedFile` artifacts from the new scope-based resolution
180
+ * pipeline (RFC #909 Ring 2). Empty unless the file's provider implements
181
+ * `emitScopeCaptures` — default for every language today, so this is
182
+ * additive and leaves the legacy DAG untouched. Consumed by #921's
183
+ * finalize-orchestrator.
184
+ */
185
+ parsedFiles: ParsedFile[];
177
186
  skippedLanguages: Record<string, number>;
178
187
  fileCount: number;
179
188
  }
@@ -43,6 +43,7 @@ import { generateId } from '../../../lib/utils.js';
43
43
  import { preprocessImportPath } from '../import-processor.js';
44
44
  import { extractVueScript, extractTemplateComponents, isVueSetupTopLevel, } from '../vue-sfc-extractor.js';
45
45
  import { buildMethodProps, arityForIdFromInfo, typeTagForId, constTagForId, buildCollisionGroups, } from '../utils/method-props.js';
46
+ import { extractParsedFile } from '../scope-extractor-bridge.js';
46
47
  // ============================================================================
47
48
  // Worker-local parser + language map
48
49
  // ============================================================================
@@ -415,6 +416,7 @@ const processBatch = (files, onProgress) => {
415
416
  ormQueries: [],
416
417
  constructorBindings: [],
417
418
  fileScopeBindings: [],
419
+ parsedFiles: [],
418
420
  skippedLanguages: {},
419
421
  fileCount: 0,
420
422
  };
@@ -1017,11 +1019,25 @@ const processFileGroup = (files, language, queryString, result, onFileProcessed)
1017
1019
  console.warn(`Query execution failed for ${file.path}: ${err instanceof Error ? err.message : String(err)}`);
1018
1020
  continue;
1019
1021
  }
1022
+ const provider = getProvider(language);
1023
+ // RFC #909 Ring 2: produce a `ParsedFile` for the new scope-based
1024
+ // resolution pipeline. No-op (returns undefined) for every language
1025
+ // today — only fires once a provider implements `emitScopeCaptures`.
1026
+ // Runs BEFORE legacy extraction and its result is independent: a
1027
+ // failure here is caught inside `extractParsedFile` and does NOT
1028
+ // affect the legacy DAG path that follows.
1029
+ const parsedFile = extractParsedFile(provider, parseContent, file.path, (message) => {
1030
+ if (parentPort)
1031
+ parentPort.postMessage({ type: 'warning', message });
1032
+ else
1033
+ console.warn(message);
1034
+ });
1035
+ if (parsedFile !== undefined)
1036
+ result.parsedFiles.push(parsedFile);
1020
1037
  // Pre-pass: extract heritage from query matches to build parentMap for buildTypeEnv.
1021
1038
  // Heritage edges (EXTENDS/IMPLEMENTS) are created by heritage-processor which runs
1022
1039
  // in PARALLEL with call-processor, so the graph edges don't exist when buildTypeEnv
1023
1040
  // runs. This pre-pass makes parent class information available for type resolution.
1024
- const provider = getProvider(language);
1025
1041
  const fileParentMap = new Map();
1026
1042
  if (provider.heritageExtractor) {
1027
1043
  for (const match of matches) {
@@ -1804,6 +1820,7 @@ let accumulated = {
1804
1820
  ormQueries: [],
1805
1821
  constructorBindings: [],
1806
1822
  fileScopeBindings: [],
1823
+ parsedFiles: [],
1807
1824
  skippedLanguages: {},
1808
1825
  fileCount: 0,
1809
1826
  };
@@ -1830,6 +1847,7 @@ const mergeResult = (target, src) => {
1830
1847
  appendAll(target.ormQueries, src.ormQueries);
1831
1848
  appendAll(target.constructorBindings, src.constructorBindings);
1832
1849
  appendAll(target.fileScopeBindings, src.fileScopeBindings);
1850
+ appendAll(target.parsedFiles, src.parsedFiles);
1833
1851
  for (const [lang, count] of Object.entries(src.skippedLanguages)) {
1834
1852
  target.skippedLanguages[lang] = (target.skippedLanguages[lang] || 0) + count;
1835
1853
  }
@@ -1878,6 +1896,7 @@ parentPort.on('message', (msg) => {
1878
1896
  ormQueries: [],
1879
1897
  constructorBindings: [],
1880
1898
  fileScopeBindings: [],
1899
+ parsedFiles: [],
1881
1900
  skippedLanguages: {},
1882
1901
  fileCount: 0,
1883
1902
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitnexus",
3
- "version": "1.6.3-rc.11",
3
+ "version": "1.6.3-rc.13",
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",