From 65cfa9ee1b7e514a53f680035082756137aaee22 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Tue, 7 Jul 2026 07:12:35 -0400 Subject: [PATCH 1/2] fix: carry analyzer name+version on the neo4j :Application node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JSON envelope advertises analyzer{name,version} (#29), but the Neo4j :Application node had neither — so the two co-primary projections diverged on analyzer identity. project() now sources name/version from the same V2Application.analyzer the envelope uses (populated from ANALYZER_VERSION in src/utils/version.ts), declared in the schema catalog and regenerated into schema.neo4j.json. --- schema.neo4j.json | 4 +++- src/build/neo4j/project.ts | 4 ++++ src/build/neo4j/schema.ts | 6 +++++- test/neo4j-schema.test.ts | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/schema.neo4j.json b/schema.neo4j.json index 2fb9054..e766e83 100644 --- a/schema.neo4j.json +++ b/schema.neo4j.json @@ -14,7 +14,9 @@ "schema_version": "string", "language": "string", "max_level": "integer", - "k_limit": "integer" + "k_limit": "integer", + "name": "string", + "version": "string" } }, { diff --git a/src/build/neo4j/project.ts b/src/build/neo4j/project.ts index 7c59e54..9a7908f 100644 --- a/src/build/neo4j/project.ts +++ b/src/build/neo4j/project.ts @@ -51,6 +51,10 @@ export function project(app: V2Application, _appName?: string): GraphRows { language: app.language, max_level: app.max_level, k_limit: app.k_limit ?? null, + // Same analyzer{name,version} the JSON envelope carries (emit.ts) — the two co-primary + // projections must never diverge on analyzer identity (issue #43). + name: app.analyzer.name, + version: app.analyzer.version, })); for (const mod of Object.values(root.symbol_table)) { diff --git a/src/build/neo4j/schema.ts b/src/build/neo4j/schema.ts index 8c43efc..4319bc5 100644 --- a/src/build/neo4j/schema.ts +++ b/src/build/neo4j/schema.ts @@ -56,7 +56,11 @@ export const NODE_LABELS: NodeLabel[] = [ label: "Application", mergeLabel: "Application", key: "id", - properties: { id: "string", schema_version: "string", language: "string", max_level: "integer", k_limit: "integer" }, + properties: { + id: "string", schema_version: "string", language: "string", max_level: "integer", k_limit: "integer", + // Analyzer identity — mirrors the JSON envelope's `analyzer{name,version}` (issue #43). + name: "string", version: "string", + }, }, { label: "Module", diff --git a/test/neo4j-schema.test.ts b/test/neo4j-schema.test.ts index c22e139..35aca27 100644 --- a/test/neo4j-schema.test.ts +++ b/test/neo4j-schema.test.ts @@ -9,6 +9,7 @@ import { describe, expect, test } from "bun:test"; import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; +import pkg from "../package.json"; import { MARKER_LABELS, NODE_LABELS, @@ -92,6 +93,19 @@ describe("neo4j schema conformance", () => { }); }); +// ---- :Application analyzer identity (issue #43) ------------------------------------------------ +// The JSON envelope advertises `analyzer{name,version}` (#29); the Neo4j :Application node is the +// co-primary projection of the same envelope and must not diverge on analyzer identity. + +describe(":Application node carries analyzer identity (issue #43)", () => { + test("version matches package.json (the same source the JSON envelope's analyzer.version uses)", () => { + const appNode = rows.nodes.find((n) => n.labels.includes("Application")); + expect(appNode, "no :Application node projected").toBeDefined(); + expect(appNode!.props.version).toBe(pkg.version); + expect(appNode!.props.name).toBe("codeanalyzer-typescript"); + }); +}); + // ---- Class inheritance: EXTENDS/IMPLEMENTS (issue #33) ------------------------------------------ // dataflow-app's src/hierarchy.ts is a minimal, first-party heritage fixture: `Rectangle implements // Shape`, `Square extends Rectangle implements Labeled`. From 9dd5ea344d622b02b16e554cdc42cd6db742ae10 Mon Sep 17 00:00:00 2001 From: Rahul Krishna Date: Tue, 7 Jul 2026 07:18:58 -0400 Subject: [PATCH 2/2] fix: rename :Application analyzer props to analyzer_name/analyzer_version Bare name/version on :Application collided with project()'s _appName param and every other CanNode's bare `name`. Namespace analyzer identity the same way the JSON envelope does (analyzer{name,version}), unambiguous from the node's own identity fields. --- schema.neo4j.json | 4 ++-- src/build/neo4j/project.ts | 8 +++++--- src/build/neo4j/schema.ts | 6 ++++-- test/neo4j-schema.test.ts | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/schema.neo4j.json b/schema.neo4j.json index e766e83..8cae479 100644 --- a/schema.neo4j.json +++ b/schema.neo4j.json @@ -15,8 +15,8 @@ "language": "string", "max_level": "integer", "k_limit": "integer", - "name": "string", - "version": "string" + "analyzer_name": "string", + "analyzer_version": "string" } }, { diff --git a/src/build/neo4j/project.ts b/src/build/neo4j/project.ts index 9a7908f..198b04e 100644 --- a/src/build/neo4j/project.ts +++ b/src/build/neo4j/project.ts @@ -52,9 +52,11 @@ export function project(app: V2Application, _appName?: string): GraphRows { max_level: app.max_level, k_limit: app.k_limit ?? null, // Same analyzer{name,version} the JSON envelope carries (emit.ts) — the two co-primary - // projections must never diverge on analyzer identity (issue #43). - name: app.analyzer.name, - version: app.analyzer.version, + // projections must never diverge on analyzer identity (issue #43). Namespaced as + // analyzer_name/analyzer_version (not bare name/version) to avoid colliding with the + // app-name param (project()'s _appName) and every other CanNode's bare `name`. + analyzer_name: app.analyzer.name, + analyzer_version: app.analyzer.version, })); for (const mod of Object.values(root.symbol_table)) { diff --git a/src/build/neo4j/schema.ts b/src/build/neo4j/schema.ts index 4319bc5..4584af4 100644 --- a/src/build/neo4j/schema.ts +++ b/src/build/neo4j/schema.ts @@ -58,8 +58,10 @@ export const NODE_LABELS: NodeLabel[] = [ key: "id", properties: { id: "string", schema_version: "string", language: "string", max_level: "integer", k_limit: "integer", - // Analyzer identity — mirrors the JSON envelope's `analyzer{name,version}` (issue #43). - name: "string", version: "string", + // Analyzer identity — mirrors the JSON envelope's `analyzer{name,version}` (issue #43), + // namespaced (not bare name/version) to avoid colliding with the app-name param / every + // other CanNode's bare `name`. + analyzer_name: "string", analyzer_version: "string", }, }, { diff --git a/test/neo4j-schema.test.ts b/test/neo4j-schema.test.ts index 35aca27..0c0d813 100644 --- a/test/neo4j-schema.test.ts +++ b/test/neo4j-schema.test.ts @@ -101,8 +101,8 @@ describe(":Application node carries analyzer identity (issue #43)", () => { test("version matches package.json (the same source the JSON envelope's analyzer.version uses)", () => { const appNode = rows.nodes.find((n) => n.labels.includes("Application")); expect(appNode, "no :Application node projected").toBeDefined(); - expect(appNode!.props.version).toBe(pkg.version); - expect(appNode!.props.name).toBe("codeanalyzer-typescript"); + expect(appNode!.props.analyzer_version).toBe(pkg.version); + expect(appNode!.props.analyzer_name).toBe("codeanalyzer-typescript"); }); });