feat(kernel): R1 scaffold — napi-rs extraction kernel, buffer contract, routing + fallback, grammar-parity CI
Phase 0 of the Rust extraction-kernel migration (docs/design/ rust-kernel-migration-plan.md, now checked in with §3a recording the shipped state): - codegraph-kernel/ napi-rs crate: extractFile(path, content, language) → five flat buffers (meta/nodes/edges/refs/arena), one JS boundary crossing per file. Node ids computed Rust-side, byte-identical to generateNodeId (pinned by test vector). Reserved per-node metrics slot for the Arc 3.2 code-metrics work. - Generic .scm-driven emitter (@def.<kind>/@name/@ref.<kind> captures, byte-range scope stack → ::-joined qualified names, contains edges, refs attributed to the innermost enclosing symbol). Seed TS/JS queries are smoke-level; R2 replaces them with the full port. - Routing seam in extractFromSource with per-file wasm fallback. DEFAULT_ROUTED is empty — no behavior change until a language passes its equivalence gate (R3). Dev opt-in: CODEGRAPH_KERNEL_LANGS. Kill switch: CODEGRAPH_KERNEL=0. Loader verifies ABI + kind tables before routing; EDGE_KINDS became a runtime array because kind order is now wire contract. - Grammar-source parity: vendored TS/TSX/JS wasm grammars built from the exact crate revisions (tree-sitter-typescript v0.23.2, tree-sitter-javascript v0.25.0, checked-in parser.c, ts-cli 0.25.10) — the tree-sitter-wasms builds were 2023-era, which the new kernel-grammar-parity test caught on day one. Production TS/JS parsing gets 2.5 years of grammar fixes; full suite green (2456 tests). - Build/release wiring: scripts/build-kernel.sh + npm run build:kernel; release.yml kernel prebuild matrix (continue-on-error — the kernel is optional everywhere, bundles fall back to the wasm path); bundles stage lib/kernel/codegraph-kernel.node; release job runs the kernel suites with CODEGRAPH_KERNEL_EXPECT=1. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
4efc6c70e2
commit
c5eebe6beb
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* Grammar-source parity gate (R1, migration plan §3.5).
|
||||
*
|
||||
* The native kernel compiles grammars from crates.io / vendored sources; the
|
||||
* wasm fallback loads grammars from tree-sitter-wasms / src/extraction/wasm.
|
||||
* If the two are built from different grammar revisions, a language's graph
|
||||
* would depend on WHICH path extracted it — per-language routing (and the
|
||||
* kernel-absent fallback) must be graph-neutral.
|
||||
*
|
||||
* Rather than trusting version metadata, this asserts the grammars are
|
||||
* behaviorally identical where extraction can observe them: ABI version and
|
||||
* the full node-kind and field tables, compared id by id.
|
||||
*
|
||||
* Runs wherever a kernel binary is staged (scripts/build-kernel.sh); skips
|
||||
* otherwise. CI that builds the kernel sets CODEGRAPH_KERNEL_EXPECT=1 so the
|
||||
* skip can't mask a missing build (asserted in kernel-scaffold.test.ts).
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import type { Language as WasmLanguage } from 'web-tree-sitter';
|
||||
import { getKernel, resetKernelForTests } from '../src/extraction/kernel';
|
||||
import { initGrammars, loadGrammarsForLanguages, getParser } from '../src/extraction/grammars';
|
||||
import type { Language } from '../src/types';
|
||||
|
||||
const KERNEL_PATH = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'codegraph-kernel',
|
||||
'prebuilds',
|
||||
`${process.platform}-${process.arch}`,
|
||||
'codegraph-kernel.node'
|
||||
);
|
||||
const kernelBuilt = fs.existsSync(KERNEL_PATH);
|
||||
|
||||
// Every kernel-capable language. `jsx` shares the javascript grammar on BOTH
|
||||
// paths (langs.rs mirrors WASM_GRAMMAR_FILES), so the distinct grammars are:
|
||||
const GRAMMAR_LANGUAGES: Language[] = ['typescript', 'tsx', 'javascript'];
|
||||
|
||||
describe.skipIf(!kernelBuilt)('kernel↔wasm grammar parity', () => {
|
||||
beforeAll(async () => {
|
||||
resetKernelForTests();
|
||||
await initGrammars();
|
||||
await loadGrammarsForLanguages(GRAMMAR_LANGUAGES);
|
||||
});
|
||||
|
||||
it.each(GRAMMAR_LANGUAGES)('%s: node-kind and field tables are identical', (language) => {
|
||||
const kernel = getKernel();
|
||||
expect(kernel).not.toBeNull();
|
||||
const native = kernel!.grammarInfo(language);
|
||||
expect(native, `kernel has no grammar for ${language}`).not.toBeNull();
|
||||
|
||||
const wasmLang = getParser(language)?.language as WasmLanguage | null | undefined;
|
||||
expect(wasmLang, `wasm grammar for ${language} not loaded`).toBeTruthy();
|
||||
|
||||
expect(native!.abiVersion, 'grammar ABI version').toBe(wasmLang!.abiVersion);
|
||||
expect(native!.nodeKindCount, 'node-kind count').toBe(wasmLang!.nodeTypeCount);
|
||||
expect(native!.fieldCount, 'field count').toBe(wasmLang!.fieldCount);
|
||||
|
||||
const wasmKinds: (string | null)[] = [];
|
||||
for (let i = 0; i < wasmLang!.nodeTypeCount; i++) wasmKinds.push(wasmLang!.nodeTypeForId(i));
|
||||
expect(native!.nodeKinds).toEqual(wasmKinds.map((k) => k ?? ''));
|
||||
|
||||
// Field ids are 1-based on both sides.
|
||||
const wasmFields: (string | null)[] = [];
|
||||
for (let i = 1; i <= wasmLang!.fieldCount; i++) wasmFields.push(wasmLang!.fieldNameForId(i));
|
||||
expect(native!.fieldNames).toEqual(wasmFields.map((f) => f ?? ''));
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user