Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion crates/codegraph-core/src/build_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,11 @@ fn check_version_mismatch(conn: &Connection) -> bool {
return true;
}
}
if let Some(prev_version) = get_meta("codegraph_version") {
// Compare against engine_version (the addon's own version), not
// codegraph_version (the npm package version). The JS post-processing
// overwrites codegraph_version with the npm version, which may differ
// from CARGO_PKG_VERSION — causing a perpetual full-rebuild loop (#928).
if let Some(prev_version) = get_meta("engine_version") {
if prev_version != current_version {
return true;
}
Expand Down
33 changes: 21 additions & 12 deletions src/domain/graph/builder/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,20 @@ function checkEngineSchemaMismatch(ctx: PipelineContext): void {
);
ctx.forceFullRebuild = true;
}
const prevVersion = meta('codegraph_version');
if (prevVersion && prevVersion !== CODEGRAPH_VERSION) {
info(
`Codegraph version changed (${prevVersion} → ${CODEGRAPH_VERSION}), promoting to full rebuild.`,
);
ctx.forceFullRebuild = true;
// When the native orchestrator is available, it handles its own version
// check (engine_version vs CARGO_PKG_VERSION). The JS-side codegraph_version
// may differ from the Rust addon version (npm 3.9.3 vs addon 3.9.2), so
// checking it here would force unnecessary full rebuilds and prevent the
// native orchestrator from ever running its fast incremental path (#928).
// Only check codegraph_version when falling through to the JS pipeline.
if (!ctx.nativeAvailable) {
const prevVersion = meta('codegraph_version');
if (prevVersion && prevVersion !== CODEGRAPH_VERSION) {
info(
`Codegraph version changed (${prevVersion} → ${CODEGRAPH_VERSION}), promoting to full rebuild.`,
);
ctx.forceFullRebuild = true;
}
}
Comment on lines +91 to 105
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Version gate skipped when orchestrator fails mid-run

checkEngineSchemaMismatch skips the codegraph_version check whenever ctx.nativeAvailable is true, even though the native orchestrator may subsequently fail and fall through to the JS pipeline. In that narrow scenario — native available, version bumped, orchestrator throws — the JS pipeline runs in whatever mode was already determined (potentially incremental) without the version-change promotion to full rebuild that the old code provided.

The comment says "the Rust orchestrator handles its own version check", which is true when the orchestrator succeeds. Given the warn + fall-through error handling in buildGraph, the JS pipeline does proceed without the version gate if the orchestrator throws after this check runs.

Fix in Claude Code

}

Expand Down Expand Up @@ -630,15 +638,16 @@ async function tryNativeOrchestrator(

const p = result.phases;

// Sync build_meta so JS-side version/engine checks work on next build.
// The Rust orchestrator already persists engine, engine_version,
// codegraph_version, node_count, edge_count, and last_build.
// Only write fields Rust doesn't set, plus built_at for JS callers.
// IMPORTANT: Do NOT overwrite codegraph_version here — Rust writes
// CARGO_PKG_VERSION and check_version_mismatch compares against it.
// Overwriting with CODEGRAPH_VERSION (npm version) when they differ
// causes a perpetual full-rebuild loop (#928).
setBuildMeta(ctx.db, {
engine: ctx.engineName,
engine_version: ctx.engineVersion || '',
codegraph_version: CODEGRAPH_VERSION,
schema_version: String(ctx.schemaVersion),
built_at: new Date().toISOString(),
node_count: String(result.nodeCount ?? 0),
edge_count: String(result.edgeCount ?? 0),
});

info(
Expand Down
9 changes: 7 additions & 2 deletions src/domain/graph/builder/stages/finalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,16 @@ function persistBuildMetadata(
const useNativeDb = ctx.engineName === 'native' && !!ctx.nativeDb;
if (!ctx.isFullBuild && ctx.allSymbols.size <= 3) return;
try {
// engine_version must be the actual engine version (addon version for native,
// npm version for WASM) — not always CODEGRAPH_VERSION. The Rust orchestrator's
// check_version_mismatch compares engine_version against CARGO_PKG_VERSION,
// so writing the npm version here would cause perpetual full rebuilds (#928).
const engineVer = ctx.engineVersion || CODEGRAPH_VERSION;
if (useNativeDb) {
ctx.nativeDb!.setBuildMeta(
Object.entries({
engine: ctx.engineName,
engine_version: CODEGRAPH_VERSION,
engine_version: engineVer,
codegraph_version: CODEGRAPH_VERSION,
schema_version: String(ctx.schemaVersion),
built_at: buildNow.toISOString(),
Expand All @@ -97,7 +102,7 @@ function persistBuildMetadata(
} else {
setBuildMeta(ctx.db, {
engine: ctx.engineName,
engine_version: CODEGRAPH_VERSION,
engine_version: engineVer,
codegraph_version: CODEGRAPH_VERSION,
schema_version: String(ctx.schemaVersion),
built_at: buildNow.toISOString(),
Expand Down
17 changes: 9 additions & 8 deletions tests/integration/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -490,17 +490,18 @@ describe('version/engine mismatch auto-promotes to full rebuild', () => {
);
db2.close();

// codegraph_version must match the npm package version
const pkg = JSON.parse(
fs.readFileSync(path.join(__dirname, '..', '..', 'package.json'), 'utf8'),
);
expect(meta.codegraph_version).toBe(pkg.version);

// engine must be either 'native' or 'wasm' (not empty, not stale)
expect(['native', 'wasm']).toContain(meta.engine);

// engine_version must equal the npm package version (#751: was using Rust crate version)
expect(meta.engine_version).toBe(pkg.version);
// engine_version must be a valid semver string. For native engine, this is
// the addon version (CARGO_PKG_VERSION); for WASM, the npm package version.
// They may differ when the addon hasn't been republished yet (#928).
expect(meta.engine_version).toMatch(/^\d+\.\d+\.\d+/);

// codegraph_version tracks the npm package version when the JS pipeline
// finalizes, or the addon version when the Rust orchestrator runs.
// Either is valid — the key invariant is it's a valid semver string.
expect(meta.codegraph_version).toMatch(/^\d+\.\d+\.\d+/);
Comment on lines +499 to +504
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Semver regex missing end anchor

Both regexes match any string that starts with a semver prefix — e.g. "3.9.3-corrupted" or "3.9.3\nunexpected" would pass. Adding $ prevents false positives and better documents the invariant.

Suggested change
expect(meta.engine_version).toMatch(/^\d+\.\d+\.\d+/);
// codegraph_version tracks the npm package version when the JS pipeline
// finalizes, or the addon version when the Rust orchestrator runs.
// Either is valid — the key invariant is it's a valid semver string.
expect(meta.codegraph_version).toMatch(/^\d+\.\d+\.\d+/);
expect(meta.engine_version).toMatch(/^\d+\.\d+\.\d+$/);
// codegraph_version tracks the npm package version when the JS pipeline
// finalizes, or the addon version when the Rust orchestrator runs.
// Either is valid — the key invariant is it's a valid semver string.
expect(meta.codegraph_version).toMatch(/^\d+\.\d+\.\d+$/);

Fix in Claude Code


// built_at must be a valid ISO timestamp from the current build
expect(new Date(meta.built_at).getTime()).toBeGreaterThan(0);
Expand Down
Loading