diff --git a/gitnexus/package-lock.json b/gitnexus/package-lock.json index b9a2b9dd6..f226aadf2 100644 --- a/gitnexus/package-lock.json +++ b/gitnexus/package-lock.json @@ -23,7 +23,7 @@ "hasInstallScript": true, "license": "PolyForm-Noncommercial-1.0.0", "dependencies": { - "@ladybugdb/core": "0.21.1", + "@ladybugdb/core": "0.21.2", "@modelcontextprotocol/sdk": "^1.0.0", "@scarf/scarf": "^1.4.0", "busboy": "^1.6.0", @@ -737,9 +737,9 @@ } }, "node_modules/@ladybugdb/core": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core/-/core-0.21.1.tgz", - "integrity": "sha512-G0LWyn/dIqX9yHlyjpR12Rvou2o1dvCB9EkuzGa5ykCYbzur8EQhRUbi4fw3FlbckWvR6rK/tVbnFpBcRLwiHg==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core/-/core-0.21.2.tgz", + "integrity": "sha512-pnD1p5AaxEdmqJsLtF6bYuSmbe+pHzmaxDPNhROFOx+f7eE/AUPJFhW+rebMD6PQxK40B2NhqXbdsPCJ+FLuxg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -748,17 +748,17 @@ "node-addon-api": "^6.0.0" }, "optionalDependencies": { - "@ladybugdb/core-darwin-arm64": "0.21.1", - "@ladybugdb/core-darwin-x64": "0.21.1", - "@ladybugdb/core-linux-arm64": "0.21.1", - "@ladybugdb/core-linux-x64": "0.21.1", - "@ladybugdb/core-win32-x64": "0.21.1" + "@ladybugdb/core-darwin-arm64": "0.21.2", + "@ladybugdb/core-darwin-x64": "0.21.2", + "@ladybugdb/core-linux-arm64": "0.21.2", + "@ladybugdb/core-linux-x64": "0.21.2", + "@ladybugdb/core-win32-x64": "0.21.2" } }, "node_modules/@ladybugdb/core-darwin-arm64": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-arm64/-/core-darwin-arm64-0.21.1.tgz", - "integrity": "sha512-TEFYNqBdbIojf1t29p3esgRhMDm56lD5eK91dwWFZJTVWAZoGXn+yLKs0NUQgj3lW2ZquuRTLZGglPhFEsDL1Q==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-arm64/-/core-darwin-arm64-0.21.2.tgz", + "integrity": "sha512-xJrPBfdh+i51SVuPlj0qgve2HQloCqPomMh01RHKpRKl/pmrwj7dU+wnK+PDVb1OCAq5MAjt8SOiChvY89Wfiw==", "cpu": [ "arm64" ], @@ -769,9 +769,9 @@ ] }, "node_modules/@ladybugdb/core-darwin-x64": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-x64/-/core-darwin-x64-0.21.1.tgz", - "integrity": "sha512-w9p3oKrqeMOeWpX3xdCc/hQJHfrZtpB9We+Ir+0qyls68dSQoeXXR1GZYzzTElOtxr4OM5USwYbVyxADx+CMZg==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-darwin-x64/-/core-darwin-x64-0.21.2.tgz", + "integrity": "sha512-zF+iONU8WOtzj9OLNr1CewQMcoJ+gwO3TJ0H4Lz+TA9D7JEdcTiHcdIQD4HQoJKEnKOwhL33MkWBdst8SfcV0g==", "cpu": [ "x64" ], @@ -782,9 +782,9 @@ ] }, "node_modules/@ladybugdb/core-linux-arm64": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-arm64/-/core-linux-arm64-0.21.1.tgz", - "integrity": "sha512-TodWmzOHxmmKvXXo9buBXGeyolxf/NqBUbo/LcR/tuMViKhGH8TBzIXi8DfVVwmsJ+CQCU065TvXgv2JoHAlZg==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-arm64/-/core-linux-arm64-0.21.2.tgz", + "integrity": "sha512-t/Z2ESuwx6RaEruZzGFrPHX19PEz5vKRVCm5fmSr85DqLd8/W5KWcw4f/RUpQtHdU2gdYLKkBur8yEpn36wNZA==", "cpu": [ "arm64" ], @@ -795,9 +795,9 @@ ] }, "node_modules/@ladybugdb/core-linux-x64": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-x64/-/core-linux-x64-0.21.1.tgz", - "integrity": "sha512-hCcLYv8ds0x4fkTaRkuc4f75KLdH9e0EV51ne6uBNdQSY7kyc0RgSoUG3utUGfFDMMkfgIJAeIxQY4ck9jQiXg==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-linux-x64/-/core-linux-x64-0.21.2.tgz", + "integrity": "sha512-U4cAfkmPjixDURzyrkYG9NTzjEJpitaTOOATGWLTl78coc3oLpIil71wN+y2e0UuM4yd7450iLTyzL2QFzUazg==", "cpu": [ "x64" ], @@ -808,9 +808,9 @@ ] }, "node_modules/@ladybugdb/core-win32-x64": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/@ladybugdb/core-win32-x64/-/core-win32-x64-0.21.1.tgz", - "integrity": "sha512-nBS1XSRJfpexdlhkezmEiRtkUt2qEDp4zBjIhH+oxgRdD7UsRz1Jyit8foJuZAmUsoQGukN5LO+YeyDU2WZTyA==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@ladybugdb/core-win32-x64/-/core-win32-x64-0.21.2.tgz", + "integrity": "sha512-O4rQve2RcXPbEPv222N2OwM5p6TgWYeQ0vT+BlhBfo8cOxvFrXkI45gl6qbH5tKlBdGDLV2ijKyrb1hoAFx4Zg==", "cpu": [ "x64" ], diff --git a/gitnexus/package.json b/gitnexus/package.json index be3ad56f9..992a891e0 100644 --- a/gitnexus/package.json +++ b/gitnexus/package.json @@ -67,7 +67,7 @@ "version": "node scripts/sync-plugin-manifests.mjs" }, "dependencies": { - "@ladybugdb/core": "0.21.1", + "@ladybugdb/core": "0.21.2", "@modelcontextprotocol/sdk": "^1.0.0", "@scarf/scarf": "^1.4.0", "busboy": "^1.6.0", diff --git a/gitnexus/test/integration/lbug-delete-nodes-for-files.test.ts b/gitnexus/test/integration/lbug-delete-nodes-for-files.test.ts index 5686ace62..dffed4eda 100644 --- a/gitnexus/test/integration/lbug-delete-nodes-for-files.test.ts +++ b/gitnexus/test/integration/lbug-delete-nodes-for-files.test.ts @@ -566,3 +566,82 @@ withTestLbugDB('embedding-row-dml-vector-gate', (handle) => { }, 120_000); }); }); + +/** + * Delete-all-then-recopy of a table that spans more than one native vector. + * + * @ladybugdb/core 0.21.0 and 0.21.1 returned every row of such a table twice + * once the incremental writeback had DETACH DELETEd it and a COPY into an + * earlier table had run: `NodeGroup::scan` let a morsel that ended before the + * group's first live row wrap an unsigned subtraction and rescan to the end of + * the group (LadybugDB #1085, fixed in 0.21.2). No primary-key error fires, so + * the only symptom was `Graph identity reconciliation failed … duplicate ID` + * on repos with more than 2048 symbols of one label (Swift structs in the + * report that found it), and 2×-inflated counts for anything that scanned. + * + * The statement order below is the one `runFullAnalysisInner` issues: delete the + * write set's rows, then COPY File before the symbol tables. + */ +withTestLbugDB('delete-recopy-multi-vector-table', (handle) => { + describe('deleteNodesForFiles then loadGraphToLbug on a multi-vector table', () => { + // One native vector holds 2048 rows; 2049 is the smallest table that spans two. + const STRUCT_COUNT = 2049; + const STRUCTS_PER_FILE = 64; + const fileOf = (i: number): string => `Sources/S${Math.floor(i / STRUCTS_PER_FILE)}.swift`; + + const buildStructGraph = () => { + const nodes: TestNodeInput[] = []; + const rels: TestRelInput[] = []; + const files = new Set(); + for (let i = 0; i < STRUCT_COUNT; i++) { + const fp = fileOf(i); + if (!files.has(fp)) { + files.add(fp); + nodes.push({ id: `File:${fp}`, label: 'File', name: path.basename(fp), filePath: fp }); + } + const id = `Struct:${fp}:S${i}`; + nodes.push({ + id, + label: 'Struct', + name: `S${i}`, + filePath: fp, + startLine: 1, + endLine: 3, + }); + rels.push({ sourceId: `File:${fp}`, targetId: id, type: 'CONTAINS' }); + } + return { graph: buildTestGraph(nodes, rels), filePaths: [...files] }; + }; + + it('returns each struct and its edge exactly once after the rows were deleted and re-copied', async () => { + const { loadGraphToLbug, deleteNodesForFiles, executeQuery } = + await import('../../src/core/lbug/lbug-adapter.js'); + const storagePath = path.dirname(handle.dbPath); + const structIds = async (): Promise => + ((await executeQuery('MATCH (n:Struct) RETURN n.id AS id')) as Array<{ id: string }>).map( + (r) => String(r.id), + ); + const containsEdges = async (): Promise => { + const rows = (await executeQuery( + `MATCH (:File)-[r:CodeRelation]->(:Struct) WHERE r.type = 'CONTAINS' RETURN count(r) AS c`, + )) as Array<{ c: number | bigint }>; + return Number(rows[0]?.c ?? 0); + }; + + const { graph, filePaths } = buildStructGraph(); + await loadGraphToLbug(graph, '/tmp/repo', storagePath); + expect(new Set(await structIds()).size).toBe(STRUCT_COUNT); + + // Every file is in the write set, as when a Swift edit pulls in all importers. + await deleteNodesForFiles(filePaths); + expect(await structIds()).toEqual([]); + + await loadGraphToLbug(graph, '/tmp/repo', storagePath); + + const ids = await structIds(); + expect(ids).toHaveLength(STRUCT_COUNT); + expect(new Set(ids).size).toBe(STRUCT_COUNT); + expect(await containsEdges()).toBe(STRUCT_COUNT); + }, 120_000); + }); +}); diff --git a/gitnexus/vendor/lbug-fts/manifest.json b/gitnexus/vendor/lbug-fts/manifest.json index 62c4da0c7..21e8d8b08 100644 --- a/gitnexus/vendor/lbug-fts/manifest.json +++ b/gitnexus/vendor/lbug-fts/manifest.json @@ -1,5 +1,5 @@ { - "coreVersion": "0.21.1", + "coreVersion": "0.21.2", "extensionVersion": "0.21.0", "filename": "libfts.lbug_extension", "officialRepo": "https://extension.ladybugdb.com/",