From 1f7764c49baae7901f8c6308927dffbb8e1d41de Mon Sep 17 00:00:00 2001 From: Abhigyan Patwari <126312502+abhigyanpatwari@users.noreply.github.com> Date: Sat, 21 Mar 2026 04:00:16 +0530 Subject: [PATCH] fix: register Section in NODE_TABLES and NODE_SCHEMA_QUERIES (#401) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add markdown file indexing (headings + cross-links) Parse .md/.mdx files using regex (no tree-sitter dependency) to extract: - Section nodes from headings (h1-h6) with hierarchy via CONTAINS edges - Cross-file IMPORTS edges from markdown links to other repo files Ported from #286 to resolve conflicts with kuzu→lbug rename. Co-Authored-By: Dennis Palatov Co-Authored-By: Claude Opus 4.6 (1M context) * fix: add Section to NODE_TABLES and NODE_SCHEMA_QUERIES The Section schema was defined but not registered in NODE_TABLES or NODE_SCHEMA_QUERIES, so the table was never created in the database. Also adds missing FROM File TO Section relation entry. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: update schema test counts for Section node type NODE_TABLES: 27→28, NODE_SCHEMA_QUERIES: 27→28, SCHEMA_QUERIES: 29→30 Co-Authored-By: Claude Opus 4.6 (1M context) * test: add diagnostic output to skills-e2e idempotency test Show stdout/stderr in assertion message so CI failures reveal why the second analyze --skills run exits with code 1. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: add Section COPY query with level column in lbug-adapter Section table has 8 columns (includes level) but getCopyQuery fell through to the default 7-column multi-language path. Adds explicit Section cases to getCopyQuery and insertNodeToLbug/upsertNodeToLbug. Error was: COPY failed for Section: Number of columns mismatch. Expected 7 but got 8. --------- Co-authored-by: Dennis Palatov --- gitnexus/src/core/lbug/lbug-adapter.ts | 9 +++++++++ gitnexus/src/core/lbug/schema.ts | 5 ++++- gitnexus/test/integration/skills-e2e.test.ts | 12 ++++++++++-- gitnexus/test/unit/schema.test.ts | 6 +++--- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/gitnexus/src/core/lbug/lbug-adapter.ts b/gitnexus/src/core/lbug/lbug-adapter.ts index bd4061797..ff8345d06 100644 --- a/gitnexus/src/core/lbug/lbug-adapter.ts +++ b/gitnexus/src/core/lbug/lbug-adapter.ts @@ -336,6 +336,9 @@ const getCopyQuery = (table: NodeTableName, filePath: string): string => { if (table === 'Process') { return `COPY ${t}(id, label, heuristicLabel, processType, stepCount, communities, entryPointId, terminalId) FROM "${filePath}" ${COPY_CSV_OPTS}`; } + if (table === 'Section') { + return `COPY ${t}(id, name, filePath, startLine, endLine, level, content, description) FROM "${filePath}" ${COPY_CSV_OPTS}`; + } if (table === 'Method') { return `COPY ${t}(id, name, filePath, startLine, endLine, isExported, content, description, parameterCount, returnType) FROM "${filePath}" ${COPY_CSV_OPTS}`; } @@ -380,6 +383,9 @@ export const insertNodeToLbug = async ( query = `CREATE (n:File {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, content: ${escapeValue(properties.content || '')}})`; } else if (label === 'Folder') { query = `CREATE (n:Folder {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}})`; + } else if (label === 'Section') { + const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : ''; + query = `CREATE (n:Section {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, level: ${properties.level || 1}, content: ${escapeValue(properties.content || '')}${descPart}})`; } else if (TABLES_WITH_EXPORTED.has(label)) { const descPart = properties.description ? `, description: ${escapeValue(properties.description)}` : ''; query = `CREATE (n:${t} {id: ${escapeValue(properties.id)}, name: ${escapeValue(properties.name)}, filePath: ${escapeValue(properties.filePath)}, startLine: ${properties.startLine || 0}, endLine: ${properties.endLine || 0}, isExported: ${!!properties.isExported}, content: ${escapeValue(properties.content || '')}${descPart}})`; @@ -451,6 +457,9 @@ export const batchInsertNodesToLbug = async ( query = `MERGE (n:File {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.content = ${escapeValue(properties.content || '')}`; } else if (label === 'Folder') { query = `MERGE (n:Folder {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}`; + } else if (label === 'Section') { + const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : ''; + query = `MERGE (n:Section {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.level = ${properties.level || 1}, n.content = ${escapeValue(properties.content || '')}${descPart}`; } else if (TABLES_WITH_EXPORTED.has(label)) { const descPart = properties.description ? `, n.description = ${escapeValue(properties.description)}` : ''; query = `MERGE (n:${t} {id: ${escapeValue(properties.id)}}) SET n.name = ${escapeValue(properties.name)}, n.filePath = ${escapeValue(properties.filePath)}, n.startLine = ${properties.startLine || 0}, n.endLine = ${properties.endLine || 0}, n.isExported = ${!!properties.isExported}, n.content = ${escapeValue(properties.content || '')}${descPart}`; diff --git a/gitnexus/src/core/lbug/schema.ts b/gitnexus/src/core/lbug/schema.ts index c08292466..a47aa9674 100644 --- a/gitnexus/src/core/lbug/schema.ts +++ b/gitnexus/src/core/lbug/schema.ts @@ -13,7 +13,7 @@ // NODE TABLE NAMES // ============================================================================ export const NODE_TABLES = [ - 'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process', + 'File', 'Folder', 'Function', 'Class', 'Interface', 'Method', 'CodeElement', 'Community', 'Process', 'Section', // Multi-language support 'Struct', 'Enum', 'Macro', 'Typedef', 'Union', 'Namespace', 'Trait', 'Impl', 'TypeAlias', 'Const', 'Static', 'Property', 'Record', 'Delegate', 'Annotation', 'Constructor', 'Template', 'Module' @@ -238,6 +238,7 @@ CREATE REL TABLE ${REL_TABLE_NAME} ( FROM File TO \`Constructor\`, FROM File TO \`Template\`, FROM File TO \`Module\`, + FROM File TO Section, FROM Folder TO Folder, FROM Folder TO File, FROM Function TO Function, @@ -462,6 +463,8 @@ export const NODE_SCHEMA_QUERIES = [ CONSTRUCTOR_SCHEMA, TEMPLATE_SCHEMA, MODULE_SCHEMA, + // Markdown support + SECTION_SCHEMA, ]; export const REL_SCHEMA_QUERIES = [ diff --git a/gitnexus/test/integration/skills-e2e.test.ts b/gitnexus/test/integration/skills-e2e.test.ts index 31f836179..58a98e984 100644 --- a/gitnexus/test/integration/skills-e2e.test.ts +++ b/gitnexus/test/integration/skills-e2e.test.ts @@ -2389,8 +2389,16 @@ export function createEntry(level: string, msg: string) { /* CI timeout tolerance */ if (result1.status === null || result2.status === null) return; - expect(result1.status).toBe(0); - expect(result2.status).toBe(0); + expect(result1.status, [ + `first analyze --skills exited with code ${result1.status}`, + `stdout: ${result1.stdout?.slice(0, 500)}`, + `stderr: ${result1.stderr?.slice(0, 500)}`, + ].join('\n')).toBe(0); + expect(result2.status, [ + `second analyze --skills exited with code ${result2.status}`, + `stdout: ${result2.stdout?.slice(0, 500)}`, + `stderr: ${result2.stderr?.slice(0, 500)}`, + ].join('\n')).toBe(0); const generatedDir = path.join(tmpDir, '.claude', 'skills', 'generated'); expect(fs.existsSync(generatedDir)).toBe(true); diff --git a/gitnexus/test/unit/schema.test.ts b/gitnexus/test/unit/schema.test.ts index 87e369652..75235f0e4 100644 --- a/gitnexus/test/unit/schema.test.ts +++ b/gitnexus/test/unit/schema.test.ts @@ -40,7 +40,7 @@ describe('LadybugDB Schema', () => { it('has expected total count', () => { // 9 core + 18 multi-language = 27 - expect(NODE_TABLES).toHaveLength(27); + expect(NODE_TABLES).toHaveLength(28); }); }); @@ -164,7 +164,7 @@ describe('LadybugDB Schema', () => { describe('schema query ordering', () => { it('NODE_SCHEMA_QUERIES has correct count', () => { - expect(NODE_SCHEMA_QUERIES).toHaveLength(27); + expect(NODE_SCHEMA_QUERIES).toHaveLength(28); }); it('REL_SCHEMA_QUERIES has one relation table', () => { @@ -173,7 +173,7 @@ describe('LadybugDB Schema', () => { it('SCHEMA_QUERIES includes all node + rel + embedding schemas', () => { // 27 node + 1 rel + 1 embedding = 29 - expect(SCHEMA_QUERIES).toHaveLength(29); + expect(SCHEMA_QUERIES).toHaveLength(30); }); it('node schemas come before relation schemas in SCHEMA_QUERIES', () => {