Skip to content

feat: add AWS Neptune as alternative graph database backend#360

Closed
naicud wants to merge 16 commits into
abhigyanpatwari:mainfrom
naicud:pr/aws-neptune-backend
Closed

feat: add AWS Neptune as alternative graph database backend#360
naicud wants to merge 16 commits into
abhigyanpatwari:mainfrom
naicud:pr/aws-neptune-backend

Conversation

@naicud

@naicud naicud commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds AWS Neptune as an alternative graph database backend alongside the default KuzuDB (LadybugDB). This enables cloud-native graph storage for teams that need shared, scalable infrastructure.

Key features

  • NeptuneAdapter — read queries via openCypher using @aws-sdk/client-neptunedata
  • Neptune ingestion — batch UNWIND with 500-node batches for efficient graph loading
  • Neptune vector search — semantic search support for Neptune-backed repos
  • CLI integration--db neptune --neptune-endpoint <host> --neptune-region <region> flags on gitnexus analyze
  • Server API/api/db/test endpoint for connection testing, PATCH /api/repo/db for config updates
  • Settings panel — Database Backend section with Neptune endpoint/region/port inputs and test connection button
  • Cypher console — New CypherConsole.tsx component for direct query execution
  • Auth — IAM+SigV4 via AWS SDK credential chain (env vars or instance profile)

Architecture

KuzuDB code is completely untouched — Neptune is additive. The local-backend.ts uses a runQuery/runParameterized abstraction that dispatches to either KuzuDB or Neptune based on repo config.

Changes (19 files)

Area Key files What
DB layer interfaces.ts, neptune-adapter.ts, neptune-ingest.ts, neptune-vector-search.ts Neptune read/write/search
CLI analyze.ts, index.ts Neptune flags
Server api.ts DB test + config endpoints, Neptune dispatch
MCP local-backend.ts Query abstraction, Neptune BM25 fallback
Storage repo-manager.ts DbConfig in registry, getDbConfig/updateRepoDb
Web SettingsPanel.tsx, backend.ts, CypherConsole.tsx Neptune config UI, Cypher console
Tests 3 unit test files Neptune adapter, ingest, vector search

Test plan

  • Build passes: cd gitnexus && npm run build && cd ../gitnexus-web && npm run build
  • Configure Neptune endpoint in Settings > Database Backend
  • Test connection button validates Neptune cluster reachability
  • gitnexus analyze --db neptune --neptune-endpoint <host> indexes to Neptune
  • Query results return from Neptune backend
  • Cypher console executes queries against Neptune

🤖 Generated with Claude Code

@vercel

vercel Bot commented Mar 18, 2026

Copy link
Copy Markdown

@naicud is attempting to deploy a commit to the NexusCore Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions

Copy link
Copy Markdown
Contributor

CI Report

All checks passed

Pipeline Status

Stage Status Details
✅ Typecheck success tsc --noEmit
✅ Unit Tests success 3 platforms
✅ Integration success 3 OS x 4 groups = 12 jobs

Test Results

Suite Tests Passed Failed Skipped Duration
Unit 1608 1607 0 1 9s
Integration 1202 1182 0 19 52s
Total 2810 2789 0 20 61s

✅ All 2789 tests passed

20 test(s) skipped — expand for details

Integration:

  • Python match/case as-pattern type binding > resolves u.save() to User#save via match/case as-pattern binding
  • Python match/case as-pattern type binding > does NOT resolve u.save() to Repo#save (negative disambiguation)
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()

Unit:

  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature

Code Coverage

Combined (Unit + Integration)

Metric Coverage Covered Base Delta Status
Statements 61.01% 7117/11665 47.27% 📈 +13.7 🟢 ████████████░░░░░░░░
Branches 54.97% 4836/8797 42.26% 📈 +12.7 🟢 ██████████░░░░░░░░░░
Functions 60.57% 616/1017 46.38% 📈 +14.2 🟢 ████████████░░░░░░░░
Lines 62.95% 6369/10117 48.88% 📈 +14.1 🟢 ████████████░░░░░░░░
Coverage breakdown by test suite

Unit Tests

Metric Coverage Covered Base Delta Status
Statements 46.36% 5408/11665 47.27% 📉 -0.9 🔴 █████████░░░░░░░░░░░
Branches 40.85% 3594/8797 42.26% 📉 -1.4 🔴 ████████░░░░░░░░░░░░
Functions 46.21% 470/1017 46.38% 📉 -0.2 🔴 █████████░░░░░░░░░░░
Lines 47.95% 4852/10117 48.88% 📉 -0.9 🔴 █████████░░░░░░░░░░░

Integration Tests

Metric Coverage Covered Base Delta Status
Statements 45.53% 5312/11665 47.27% 📉 -1.7 🔴 █████████░░░░░░░░░░░
Branches 43.11% 3793/8797 42.26% 📈 +0.9 🟢 ████████░░░░░░░░░░░░
Functions 42.96% 437/1017 46.38% 📉 -3.4 🔴 ████████░░░░░░░░░░░░
Lines 46.55% 4710/10117 48.88% 📉 -2.3 🔴 █████████░░░░░░░░░░░

📋 View full run · Generated by CI

@magyargergo

Copy link
Copy Markdown
Collaborator

@claude ultra-think while reviewing this PR as a senior backend engineer and see if we are backward compatible wth these changes architectural fit. Also we prefer simplicity over overengineering!

Repository owner deleted a comment from claude Bot Mar 18, 2026
@abhigyanpatwari

Copy link
Copy Markdown
Owner

Technical Review

CRITICAL

1. Three executeQuery calls not migrated to runQuery — Neptune repos crash during impact analysis

local-backend.ts:1446-1460 — The Promise.all block in impact enrichment still calls raw executeQuery(), bypassing Neptune dispatch:

const [processRows, moduleRows, directModuleRows] = await Promise.all([
  executeQuery(repo.id, `...`).catch(() => []),   // line 1446
  executeQuery(repo.id, `...`).catch(() => []),   // line 1453
  d1Ids ? executeQuery(repo.id, `...`).catch(() => []) : ...  // line 1460
]);

These hit LadybugDB even for Neptune-backed repos. Since LadybugDB is never initialized for Neptune repos, the .catch(() => []) silently swallows failures — affected processes and modules will always be empty for Neptune repos.

2. Cypher injection in api.ts Neptune search endpoint

WHERE toLower(n.name) CONTAINS toLower('${query.replace(/'/g, "\'")}')

String interpolation with wrong escaping (\' instead of Cypher's ''). Should use a parameterized query:

WHERE toLower(n.name) CONTAINS toLower($query)

3. CREATE INDEX ON silently fails on Neptune

neptune-ingest.ts:185:

await sendCypher(client, `CREATE INDEX ON :\`${lbl}\`(id)`);

Neptune does not support CREATE INDEX via openCypher — this is Neo4j DDL syntax. The try/catch swallows the error, so no indexes are created. The comment "Create indexes BEFORE edges — critical for MATCH performance" is misleading.


HIGH

4. Full-scan CONTAINS on n.content will timeout on Neptune

Neptune BM25 fallback in both local-backend.ts and api.ts:

WHERE toLower(n.name) CONTAINS toLower($q) OR toLower(n.content) CONTAINS toLower($q)

This applies toLower + string CONTAINS over every node's source code content. For any non-trivial codebase this will exceed Neptune's 120s query timeout. Drop n.content for Neptune or add a size guard.

5. NOT exists(n._gen) — Neptune openCypher compatibility

neptune-ingest.ts cleanup query:

WHERE n._gen <> '${generation}' OR NOT exists(n._gen)

exists(n.property) behavior varies by Neptune engine version. Some versions require n._gen IS NOT NULL instead. Also: generation is string-interpolated (safe since internally generated, but should use parameters for consistency).

6. tree-sitter-swift removed from package.json

Unrelated change — this removes Swift language parsing support and should be in a separate PR.


MEDIUM

7. loadEmbeddingsToNeptune defined but never called during analyze

The function exists in neptune-ingest.ts but analyze.ts never calls it for Neptune repos when --embeddings is enabled. Vectors get generated but never stored. The Neptune semantic search path will always find an empty cache.

8. Stale (entry as any).db type cast

local-backend.ts refreshRepos:

db: (entry as any).db,

RegistryEntry already has db?: DbConfig in the updated interface. The as any cast is unnecessary and hides type errors.

9. No Neptune embedding cache invalidation after re-indexing

invalidateNeptuneEmbeddingCache() is exported from neptune-vector-search.ts but never called. If the MCP server is running during re-index, semantic searches use stale embeddings.


LOW

10. Cypher Console doesn't indicate which backend is active — users won't know whether to write KuzuDB or Neptune syntax.

11. RefreshCw icon used in SettingsPanel reload banner — verify the import exists (not visible in the diff).

12. Neptune adapter config is privateneptune-vector-search.ts uses ugly (adapter as unknown as { config?: ... }).config casts for cache keys. Expose an identifier or the config.

13. Edge batch size of 25 is very small — 5000+ relationships = 200+ HTTP round-trips. Neptune can handle 100-250 per batch for simple MERGE patterns.

@abhigyanpatwari

abhigyanpatwari commented Mar 18, 2026

Copy link
Copy Markdown
Owner

@naicud Check the above technical issues. Technical issues can be solved but here is some deeper issue to give u context of what could go wrong.

While implementing the mcp tools to query Kuzu, I had to build sort of abstractions that shield the LLM from dialect entirely, or at least guide it, because kuzuDB isn't as popular as Neo4j and LLMs kept defaulting to Neo4j like syntax ( probably due to their training data being richer for neo ). Now the difficult part was finding the sweet spot which prevented over-prompting to eat away LLM flexibility and prevent the wrong query generation or atleast induce the error message with guide to let the llm know what went wrong and how it can fix it in the next tool call.

So you would need to do some hit and trial test to see if the queries are executing without issues and feels like same level of quality.

cc: @magyargergo

@magyargergo

Copy link
Copy Markdown
Collaborator

This is an interesting area. I think we need to reevaluate this, since we migrated to ladybug and they might have improved on this.

naicud and others added 12 commits March 19, 2026 17:15
- NeptuneAdapter for read queries (openCypher via @aws-sdk/client-neptunedata)
- Neptune ingestion pipeline (batch UNWIND, 500-node batches)
- Neptune vector search support
- CLI flags: --db neptune, --neptune-endpoint, --neptune-region
- Server API: /api/db/test, PATCH /api/repo/db
- Settings panel: Database Backend configuration section
- Cypher console component for direct query execution
- IAM+SigV4 auth via AWS SDK credential chain

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three executeQuery() calls in the impact enrichment Promise.all block
bypassed Neptune dispatch, causing empty results for affected processes
and modules on Neptune-backed repos.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… query

Neptune search endpoint used string interpolation with wrong escaping
(backslash-quote instead of Cypher double-quote), creating a Cypher
injection vulnerability. Now uses executeParameterized with $q param.

LIMIT remains inline as Neptune openCypher does not support parameterized
LIMIT values (must be integer literal).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Neptune does not support CREATE INDEX via openCypher (Neo4j DDL syntax).
The try/catch silently swallowed the error, creating the false impression
that indexes were being created. Neptune manages indexes automatically
via its DFE query engine.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Full-scan toLower(n.content) CONTAINS over all source code nodes
exceeds Neptune's 120s query timeout on non-trivial codebases.
Neptune search now only matches on n.name.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NOT exists(n.property) behavior varies by Neptune engine version.
Replaced with n._gen IS NULL which is standard openCypher. Also
parameterized the generation value instead of string interpolation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The embedding pipeline writes directly to LadybugDB and doesn't support
Neptune yet. Instead of silently generating embeddings that never get
stored, explicitly skip with an informative message.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
RegistryEntry already has db?: DbConfig in its interface. The as any
cast was hiding type errors. Aligned RepoHandle.db type to use DbConfig.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Neptune embedding cache was never cleared when repos were
re-indexed, causing stale semantic search results if the MCP
server was running during re-indexing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a small badge to the Cypher Console header displaying "LadybugDB" or
"Neptune" to help users write appropriate syntax for the active backend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
NeptuneAdapter.config was private but neptune-vector-search needed it
for cache keys, using ugly (adapter as unknown as ...) casts. Added a
public id getter returning endpoint:port.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
25 edges per batch caused 200+ HTTP round-trips for typical codebases.
Neptune handles 100 edges per MERGE batch comfortably. Adaptive sizing
will still halve automatically on timeout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@naicud naicud force-pushed the pr/aws-neptune-backend branch from 28ff150 to f2176b0 Compare March 19, 2026 16:16
@naicud

naicud commented Mar 19, 2026

Copy link
Copy Markdown
Contributor Author

@abhigyanpatwari I think that some fix was ported in the PR now

@magyargergo

Copy link
Copy Markdown
Collaborator

@claude ultra-think while evaluating the architectural fit of this PR. we want to support multiple datasoruces but it has to be done right otherwise the architeture will drift.

@github-actions

github-actions Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 2m 26s —— View job


Architectural Review: AWS Neptune Backend

  • Gather context and understand the request
  • Read core abstraction layer (interfaces.ts, local-backend.ts)
  • Read Neptune implementation files
  • Read storage/repo-manager changes
  • Read server API changes
  • Read CLI changes
  • Analyze architectural fit and drift risks
  • Post comprehensive review

Summary Verdict

The PR is structurally viable and backward compatible, but it has a foundational architectural problem: the IDbQueryAdapter interface was introduced but not enforced consistently. Instead of polymorphic dispatch through the interface, the implementation uses scattered if (neptune) ... else ... branches at the application layer. This will cause architectural drift as backends are added.


What Is Done Well

  • Additive, not destructive. KuzuDB/LadybugDB code is genuinely untouched. Old RegistryEntry records without a db field correctly default to lbug — solid backwards compat.
  • IDbQueryAdapter interface (interfaces.ts:31-35) is the right instinct — clean, minimal, correct contract.
  • runQuery/runParameterized (local-backend.ts:1758-1773) — good in-class dispatch layer. Every MCP tool call that was migrated routes through this correctly.
  • UNWIND+MERGE ingestion — idempotent, fault-tolerant, sensible batch sizing.
  • RegistryEntry.db?: DbConfig is the right canonical location for per-repo backend config.

Architectural Problems (Drift Risks)

1. Two Independent Dispatch Layers (No Shared Abstraction)

The most important structural problem: there are two parallel dispatch systems with no shared code.

Layer 1 — MCP path (local-backend.ts:1758-1773):

private async runQuery(repoId: string, cypher: string) {
  if (neptuneAdapters.has(repoId)) {         // keyed by module-level Map
    return neptuneAdapters.get(repoId)!.executeQuery(cypher);
  }
  return executeQuery(repoId, cypher);         // lbug global function
}

Layer 2 — HTTP API path (api.ts:29-38):

function getDbConfigFromEntry(entry) {          // duplicates repo-manager.ts:getDbConfig
  if ((entry as any).db?.type === 'kuzu') ...  // kuzu compat repeated
  return (entry as any).db ?? { type: 'lbug', ... };
}
// Then per-endpoint: if (dbConfig.type === 'neptune') { new NeptuneAdapter(...) }

getDbConfigFromEntry in api.ts is a verbatim copy of getDbConfig from repo-manager.ts. Bug fixes to the kuzulbug compat logic will only propagate to one unless both are updated. Fix this →

Adding a third backend requires updating both layers independently. This is the core drift risk.

2. IDbQueryAdapter Is Not Enforced — LadybugDB Has No Adapter Class

The interface at interfaces.ts:31 is defined but LadybugDB has no class implementing it. The pattern is:

  • Neptune → NeptuneAdapter implements IDbQueryAdapter
  • LadybugDB → module-level functions executeQuery(repoId, ...)

This means callers can never use IDbQueryAdapter polymorphically. The if (neptune) ... else ... branches exist precisely because there is no lbug adapter class. The right fix is a LbugAdapter that wraps the existing functions and implements IDbQueryAdapter, then a single getAdapter(repo): IDbQueryAdapter replaces all branching.

3. Neptune-Specific Branching Inside Tool Logic

The query tool in local-backend.ts:395-417 has:

const isNeptune = neptuneAdapters.has(repo.id);
if (isNeptune) {
  [bm25Results, semanticResults] = await Promise.all([...neptune path...]);
} else {
  [bm25Results, semanticResults] = await Promise.all([...lbug path...]);
}

This is business logic infiltrating what should be a backend-agnostic tool. If you add a third backend, every if (isNeptune) site needs a new branch. With a proper adapter pattern, this collapses to this.runParameterized(repo.id, ...) regardless of backend.

4. Write Path Has No Interface — Ingestion Is Unabstracted

loadGraphToLbug and loadGraphToNeptune are separate top-level functions with no shared contract. There is no IDbIngestAdapter or equivalent. If the graph schema evolves (new node types, new edge properties), both functions must be updated independently. This compounds as backends grow. Fix this →

5. /api/graph Endpoint Is Not Backend-Aware

api.ts:189-202 — the graph visualization endpoint calls withLbugDb(lbugPath, () => buildGraph()) unconditionally. Neptune repos will return an empty or error response from the graph visualizer. The buildGraph() function reads directly from LadybugDB schema tables (NODE_TABLES). This is a user-facing gap that will appear silently.

6. Per-Request Adapter Construction in HTTP Path

api.ts:220-228 — for Neptune repos, POST /api/query and POST /api/search construct a new NeptuneAdapter(...) and close it for every HTTP request. The MCP path correctly keeps a persistent adapter in neptuneAdapters. The HTTP path should reuse the same pattern (or route through LocalBackend).


Functional Gaps (Already Called Out in Review)

These are confirmed by reading the code:

Gap Location Impact
loadEmbeddingsToNeptune defined but never called analyze.ts — no call site Semantic search always empty for Neptune repos
Embedding cache invalidation not called after re-index neptune-vector-search.ts:221 — export unused Stale vectors on re-index
tree-sitter-swift removed package.json Unrelated, should be separate PR
/api/graph LadybugDB-only api.ts:197 Graph viz broken for Neptune repos

Recommended Path Forward

The fix that would prevent all future drift is small:

  1. Create LbugAdapter class implementing IDbQueryAdapter — wrap the existing lbug module functions.
  2. Add getAdapter(repo): IDbQueryAdapter to LocalBackend — resolves once, removes all if (neptune) branches.
  3. Delete getDbConfigFromEntry from api.ts — use the getDbConfig already in repo-manager.ts.
  4. Route /api/query through LocalBackend rather than constructing adapters directly — this also fixes the per-request construction issue.
  5. Wire loadEmbeddingsToNeptune into analyze.ts when --embeddings + --db neptune are set.

Steps 1–4 are a modest refactor that would collapse ~150 lines of duplicated dispatch down to a single resolution site. The PR is mergeable without them, but without them the next backend will double the drift.


· Branch

@naicud

naicud commented Mar 19, 2026

Copy link
Copy Markdown
Contributor Author

I'm Checking guys ;)

@zander-raycraft zander-raycraft added the enhancement New feature or request label Mar 21, 2026
naicud and others added 2 commits March 22, 2026 02:05
# Conflicts:
#	gitnexus/src/mcp/local/local-backend.ts
Enables polymorphic dispatch for LadybugDB alongside NeptuneAdapter.
Uses dependency injection to avoid circular imports between layers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
naicud and others added 2 commits March 22, 2026 02:15
- getAdapter(repoId) factory returns IDbQueryAdapter (polymorphic)
- LbugQueryAdapter for LadybugDB, NeptuneAdapter for Neptune
- Eliminates module-level neptuneAdapters Map
- runQuery/runParameterized delegate through adapter
- isNeptune() helper replaces scattered neptuneAdapters.has() checks

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Delete duplicate getDbConfigFromEntry (canonical getDbConfig in repo-manager)
- Delete buildGraph function (moved to LocalBackend.getGraphForApi)
- /api/query routes through backend.executeCypher()
- /api/search routes through new backend.searchForApi()
- /api/graph routes through new backend.getGraphForApi()
- Returns 501 for graph viz on Neptune repos (proper error)
- Eliminates per-request NeptuneAdapter construction
- Remove double closeLbug() in shutdown handler
- Update Neptune injection safety test to scan new code location

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

CI Report

All checks passed0037d7f

Pipeline

Stage Status Ubuntu Windows macOS
Typecheck success
Tests success

Tests

Metric Value
Total 3647
Passed 3627
Skipped 20
Files 1048
Duration 1m 58s

✅ All 3627 tests passed across 1048 files

20 test(s) skipped
  • buildTypeEnv > known limitations (documented skip tests) > Ruby block parameter: users.each { |user| } — closure param inference, different feature
  • Python match/case as-pattern type binding > resolves u.save() to User#save via match/case as-pattern binding
  • Python match/case as-pattern type binding > does NOT resolve u.save() to Repo#save (negative disambiguation)
  • Swift constructor-inferred type resolution > detects User and Repo classes, both with save methods
  • Swift constructor-inferred type resolution > resolves user.save() to Models/User.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > resolves repo.save() to Models/Repo.swift via constructor-inferred type
  • Swift constructor-inferred type resolution > emits exactly 2 save() CALLS edges (one per receiver type)
  • Swift self resolution > detects User and Repo classes, each with a save function
  • Swift self resolution > resolves self.save() inside User.process to User.save, not Repo.save
  • Swift parent resolution > detects BaseModel and User classes plus Serializable protocol
  • Swift parent resolution > emits EXTENDS edge: User → BaseModel
  • Swift parent resolution > emits IMPLEMENTS edge: User → Serializable (protocol conformance)
  • Swift cross-file User.init() inference > resolves user.save() via User.init(name:) inference
  • Swift cross-file User.init() inference > resolves user.greet() via User.init(name:) inference
  • Swift return type inference > detects User class and getUser function
  • Swift return type inference > detects save function on User (Swift class methods are Function nodes)
  • Swift return type inference > resolves user.save() to User#save via return type of getUser() -> User
  • Swift return-type inference via function return type > resolves user.save() to User#save via return type of getUser()
  • Swift return-type inference via function return type > user.save() does NOT resolve to Repo#save
  • Swift return-type inference via function return type > resolves repo.save() to Repo#save via return type of getRepo()

Coverage

Metric Coverage Covered Base (main) Delta
Statements 68.08% 9238/13569 68.92% 📉 -0.8%
Branches 59.28% 6218/10489 60.08% 📉 -0.8%
Functions 70.91% 829/1169 71.5% 📉 -0.6%
Lines 70.26% 8244/11732 71.16% 📉 -0.9%

📋 Full run · Coverage from Ubuntu · Generated by CI

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds AWS Neptune as an optional graph database backend alongside the existing LadybugDB (local) path, wiring it through CLI/server/MCP/web settings so repos can be configured to query Neptune via openCypher.

Changes:

  • Introduces a DB-adapter abstraction (IDbQueryAdapter) with implementations for LadybugDB and Neptune, plus Neptune ingestion and app-side vector search caching.
  • Extends CLI + server API to select/test/update DB backend config per repo, and updates MCP local backend query dispatch accordingly.
  • Adds web UI settings for Neptune configuration plus a Cypher console, and adds unit tests around the Neptune components.

Reviewed changes

Copilot reviewed 23 out of 24 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
gitnexus/src/core/db/interfaces.ts Defines DbConfig and IDbQueryAdapter contract.
gitnexus/src/core/db/lbug-query-adapter.ts Adapter wrapper for LadybugDB query functions.
gitnexus/src/core/db/neptune/neptune-adapter.ts Neptune openCypher adapter using @aws-sdk/client-neptunedata.
gitnexus/src/core/db/neptune/neptune-ingest.ts Neptune ingestion (UNWIND+MERGE batches) + stats/embedding loaders.
gitnexus/src/core/db/neptune/neptune-vector-search.ts App-side vector search cache + cosine similarity top‑K.
gitnexus/src/mcp/local/local-backend.ts Adapter-based query dispatch; Neptune fallbacks and API helpers.
gitnexus/src/server/api.ts Adds /api/db/test + /api/repo/db; routes graph/query/search through LocalBackend.
gitnexus/src/storage/repo-manager.ts Persists per-repo db config in registry and adds updateRepoDb.
gitnexus/src/cli/index.ts Adds --db and Neptune CLI flags.
gitnexus/src/cli/analyze.ts Adds Neptune ingestion path and stores db config in registry.
gitnexus/test/unit/neptune-adapter.test.ts Unit tests for NeptuneAdapter behavior (mocked AWS SDK).
gitnexus/test/unit/neptune-ingest.test.ts Unit tests for Neptune ingestion + stats (mocked AWS SDK).
gitnexus/test/unit/neptune-vector-search.test.ts Tests intended to cover vector search logic.
gitnexus/test/unit/neptune-impact-dispatch.test.ts Guards dispatch usage in impact enrichment block.
gitnexus/test/unit/neptune-content-scan.test.ts Regression test to prevent Neptune n.content scans.
gitnexus/test/unit/neptune-api-injection.test.ts Regression test to prevent string interpolation in Neptune search query params.
gitnexus/test/unit/lbug-query-adapter.test.ts Unit tests for the LadybugDB query adapter wrapper.
gitnexus-web/src/services/backend.ts Adds API calls for DB config update and Neptune connection testing.
gitnexus-web/src/core/llm/types.ts Persists DB backend settings in web settings model.
gitnexus-web/src/components/SettingsPanel.tsx Adds Neptune backend UI controls + test connection + Cypher console entrypoint.
gitnexus-web/src/components/CypherConsole.tsx New UI modal to run openCypher queries.
docs/neptune-setup.md Setup guide and operational notes for using Neptune.
gitnexus/package.json Adds @aws-sdk/client-neptunedata dependency.
gitnexus/package-lock.json Locks AWS SDK dependency graph.
Files not reviewed (1)
  • gitnexus/package-lock.json: Language not supported
Comments suppressed due to low confidence (2)

gitnexus/src/server/api.ts:117

  • The /api/graph handler returns 500 on repo resolution errors (e.g. no repos indexed, repo not found, multiple repos) even though statusFromError() exists to map these to 404/400. Consider using res.status(statusFromError(err)) here for consistent client behavior.
  // Get full graph
  app.get('/api/graph', async (req, res) => {
    try {
      const graph = await backend.getGraphForApi(requestedRepo(req));
      if (!graph) {
        res.status(501).json({ error: 'Graph visualization is not supported for Neptune-backed repos. Use Cypher queries instead.' });
        return;
      }
      res.json(graph);
    } catch (err: any) {
      res.status(500).json({ error: err.message || 'Failed to build graph' });
    }

gitnexus/src/server/api.ts:132

  • The /api/query handler similarly hard-codes a 500 response for repo resolution errors. For consistency with other endpoints, use statusFromError(err) so "not found" / "multiple repositories" produce 404/400 rather than 500.
  // Execute Cypher query
  app.post('/api/query', async (req, res) => {
    try {
      const cypher = req.body.cypher as string;
      if (!cypher) {
        res.status(400).json({ error: 'Missing "cypher" in request body' });
        return;
      }
      const result = await backend.executeCypher(requestedRepo(req), cypher);
      res.json({ result });
    } catch (err: any) {
      res.status(500).json({ error: err.message || 'Query failed' });
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread gitnexus/package.json
"prepack": "npm run build && chmod +x dist/cli/index.js"
},
"dependencies": {
"@aws-sdk/client-neptunedata": "^3.0.0",

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The newly added @aws-sdk/client-neptunedata dependency currently resolves (per package-lock) to a version that declares engines.node >=20, but this package.json declares engines.node >=18. This will likely break installs/runtime on Node 18. Consider pinning a neptunedata client version that supports Node 18, or bumping the project's Node engine requirement to >=20 to match the SDK.

Suggested change
"@aws-sdk/client-neptunedata": "^3.0.0",
"@aws-sdk/client-neptunedata": "3.624.0",

Copilot uses AI. Check for mistakes.
Comment on lines +155 to +175
const { neptuneEndpoint, neptuneRegion, neptunePort } = req.body;
if (!neptuneEndpoint || !neptuneRegion) {
res.status(400).json({ ok: false, error: 'neptuneEndpoint and neptuneRegion are required' });
return;
}
const lbugPath = path.join(entry.storagePath, 'lbug');
const parsedLimit = Number(req.body.limit ?? 10);
const limit = Number.isFinite(parsedLimit)
? Math.max(1, Math.min(100, Math.trunc(parsedLimit)))
: 10;
const config: NeptuneDbConfig = {
type: 'neptune',
endpoint: neptuneEndpoint,
region: neptuneRegion,
port: neptunePort ?? 8182,
};
const adapter = new NeptuneAdapter(config);
const t0 = Date.now();
try {
await adapter.executeQuery('MATCH (n) RETURN count(n) AS cnt LIMIT 1');
res.json({ ok: true, latencyMs: Date.now() - t0 });
} catch (err: any) {
res.json({ ok: false, error: err.message || 'Connection failed' });
} finally {
adapter.close();
}

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

In /api/db/test, neptunePort is taken directly from req.body and assigned to NeptuneDbConfig.port without parsing/validation, so a string (e.g. "8182") or invalid value could slip through. Also, adapter.close() is async but is not awaited in the finally block, which can leave cleanup unhandled. Parse/validate the port (coerce to a number with bounds/default) and await adapter.close() in finally.

Copilot uses AI. Check for mistakes.
const results = await backend.searchForApi(requestedRepo(req), query, limit);
res.json({ results });
} catch (err: any) {
res.status(500).json({ error: err.message || 'Search failed' });

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The /api/search handler hard-codes a 500 response for repo resolution errors; this makes "repo not found" look like a server failure. Use statusFromError(err) (already defined) to return 404/400 where appropriate.

Suggested change
res.status(500).json({ error: err.message || 'Search failed' });
const status = statusFromError(err);
res.status(status).json({ error: err.message || 'Search failed' });

Copilot uses AI. Check for mistakes.
Comment on lines 319 to 333
@@ -261,13 +333,17 @@ export const analyzeCommand = async (
}

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

Phase 3.5 restores cached embeddings by writing CodeEmbedding nodes via executeWithReusedStatement, but this block runs even when --db neptune is selected (when LadybugDB may not be initialized/available). This can cause Neptune indexing to fail or silently do the wrong thing. Guard this block behind !isNeptune, or (if Neptune should support embeddings) write the cached embeddings to Neptune via the Neptune embedding loader instead.

Copilot uses AI. Check for mistakes.
Comment on lines 343 to +346
if (options?.embeddings) {
if (stats.nodes > EMBEDDING_NODE_LIMIT) {
if (isNeptune) {
embeddingSkipReason = 'skipped (Neptune embedding support not yet available — use LadybugDB for embeddings)';
} else if (stats.nodes > EMBEDDING_NODE_LIMIT) {

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The Neptune path forces embeddings to be skipped even when --embeddings is set, but the PR description advertises Neptune vector/semantic search support and the runtime Neptune semanticSearch path expects embeddings on nodes. Also, loadEmbeddingsToNeptune is defined but never called anywhere. If Neptune semantic search is intended to work, the CLI should generate embeddings and persist them to Neptune; otherwise, the PR description/docs should be updated to reflect that semantic search is not supported yet.

Copilot uses AI. Check for mistakes.
Comment on lines +219 to +233
// 5. Clean up stale nodes from previous runs.
// Any node without the current generation marker is orphaned.
// Batched to avoid timeouts on large graphs. Non-fatal if it fails.
onProgress?.('Cleaning up stale nodes...');
const CLEANUP_BATCH = 10_000;
let cleanupTotal = 0;
try {
for (;;) {
const res = await client.send(new ExecuteOpenCypherQueryCommand({
openCypherQuery: `MATCH (n) WHERE n._gen <> $gen OR n._gen IS NULL WITH n LIMIT ${CLEANUP_BATCH} DETACH DELETE n RETURN count(*) AS deleted`,
parameters: JSON.stringify({ gen: generation }),
}));
const rows = (res.results as Record<string, unknown>[]) ?? [];
const deleted = Number(rows[0]?.['deleted'] ?? 0);
if (deleted === 0) break;

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The stale-node cleanup query deletes all nodes in the Neptune database that don't match the current generation marker. If a Neptune cluster is ever shared across multiple repos (or used for anything else), this will delete unrelated data. Consider scoping all GitNexus nodes by a per-repo marker (e.g. repoId property, separate label namespace, or separate database/graph per repo) and include that scope in both upsert and cleanup queries.

Copilot uses AI. Check for mistakes.
Comment on lines +744 to +769
for (const embRow of rawResults) {
const nodeId = embRow.nodeId;
const distance = embRow.distance;
const labelEndIdx = nodeId.indexOf(':');
const label = labelEndIdx > 0 ? nodeId.substring(0, labelEndIdx) : 'Unknown';
if (!VALID_NODE_LABELS.has(label)) continue;

try {
const nodeQuery = label === 'File'
? `MATCH (n:File {id: $nodeId}) RETURN n.name AS name, n.filePath AS filePath`
: `MATCH (n:\`${label}\` {id: $nodeId}) RETURN n.name AS name, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine`;
const nodeRows = await this.runParameterized(repo.id, nodeQuery, { nodeId });
if (nodeRows.length > 0) {
const nodeRow = nodeRows[0];
results.push({
nodeId,
name: nodeRow.name ?? nodeRow[0] ?? '',
type: label,
filePath: nodeRow.filePath ?? nodeRow[1] ?? '',
distance,
startLine: label !== 'File' ? (nodeRow.startLine ?? nodeRow[2]) : undefined,
endLine: label !== 'File' ? (nodeRow.endLine ?? nodeRow[3]) : undefined,
});
}
} catch {}
}

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

Neptune semanticSearch currently does an N+1 query pattern: after getting top-K embedding hits, it runs an additional Cypher query per result to fetch node metadata. Over Neptune HTTP this can add significant latency and load. Consider fetching metadata for all returned nodeIds in a single parameterized query (e.g. UNWIND $ids AS id MATCH (n {id:id}) RETURN ...), or extending the embedding fetch/cache to include the needed node fields.

Suggested change
for (const embRow of rawResults) {
const nodeId = embRow.nodeId;
const distance = embRow.distance;
const labelEndIdx = nodeId.indexOf(':');
const label = labelEndIdx > 0 ? nodeId.substring(0, labelEndIdx) : 'Unknown';
if (!VALID_NODE_LABELS.has(label)) continue;
try {
const nodeQuery = label === 'File'
? `MATCH (n:File {id: $nodeId}) RETURN n.name AS name, n.filePath AS filePath`
: `MATCH (n:\`${label}\` {id: $nodeId}) RETURN n.name AS name, n.filePath AS filePath, n.startLine AS startLine, n.endLine AS endLine`;
const nodeRows = await this.runParameterized(repo.id, nodeQuery, { nodeId });
if (nodeRows.length > 0) {
const nodeRow = nodeRows[0];
results.push({
nodeId,
name: nodeRow.name ?? nodeRow[0] ?? '',
type: label,
filePath: nodeRow.filePath ?? nodeRow[1] ?? '',
distance,
startLine: label !== 'File' ? (nodeRow.startLine ?? nodeRow[2]) : undefined,
endLine: label !== 'File' ? (nodeRow.endLine ?? nodeRow[3]) : undefined,
});
}
} catch {}
}
const nodeIds: string[] = [];
const distanceById = new Map<string, number>();
const labelById = new Map<string, string>();
for (const embRow of rawResults) {
const nodeId = embRow.nodeId as string;
const distance = embRow.distance as number;
const labelEndIdx = nodeId.indexOf(':');
const label = labelEndIdx > 0 ? nodeId.substring(0, labelEndIdx) : 'Unknown';
if (!VALID_NODE_LABELS.has(label)) continue;
nodeIds.push(nodeId);
distanceById.set(nodeId, distance);
labelById.set(nodeId, label);
}
if (nodeIds.length === 0) {
return results;
}
try {
const nodeQuery = `
UNWIND $ids AS nodeId
MATCH (n {id: nodeId})
RETURN nodeId AS nodeId,
labels(n)[0] AS dbLabel,
n.name AS name,
n.filePath AS filePath,
n.startLine AS startLine,
n.endLine AS endLine
`;
const nodeRows = await this.runParameterized(repo.id, nodeQuery, { ids: nodeIds });
const rowById = new Map<string, any>();
for (const row of nodeRows) {
if (row && row.nodeId) {
rowById.set(row.nodeId as string, row);
}
}
for (const nodeId of nodeIds) {
const nodeRow = rowById.get(nodeId);
if (!nodeRow) continue;
const label = labelById.get(nodeId) ?? (nodeRow.dbLabel as string | undefined) ?? 'Unknown';
const distance = distanceById.get(nodeId) ?? 0;
results.push({
nodeId,
name: nodeRow.name ?? nodeRow[0] ?? '',
type: label,
filePath: nodeRow.filePath ?? nodeRow[1] ?? '',
distance,
startLine: label !== 'File' ? (nodeRow.startLine ?? nodeRow[2]) : undefined,
endLine: label !== 'File' ? (nodeRow.endLine ?? nodeRow[3]) : undefined,
});
}
} catch {}

Copilot uses AI. Check for mistakes.
fetchOpenRouterModels,
} from '../core/llm/settings-service';
import type { LLMSettings, LLMProvider } from '../core/llm/types';
import { testDbConnection, updateRepoDbConfig, getBackendUrl } from '../services/backend';

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

getBackendUrl is imported here but not used anywhere in this component. If the web build uses noUnusedLocals/noUnusedParameters (or ESLint), this will fail CI or add noise. Remove the unused import or use it.

Suggested change
import { testDbConnection, updateRepoDbConfig, getBackendUrl } from '../services/backend';
import { testDbConnection, updateRepoDbConfig } from '../services/backend';

Copilot uses AI. Check for mistakes.
{dbType === 'neptune' ? 'Neptune' : 'LadybugDB'}
</span>
</div>
<button onClick={onClose} className="text-white/40 hover:text-white transition-colors">✕</button>

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

The CypherConsole close button is rendered as a bare "✕" without an accessible name. Add an aria-label (e.g. "Close") and/or visually hidden text so screen readers can announce its purpose.

Suggested change
<button onClick={onClose} className="text-white/40 hover:text-white transition-colors"></button>
<button
onClick={onClose}
className="text-white/40 hover:text-white transition-colors"
aria-label="Close Cypher Console"
>
</button>

Copilot uses AI. Check for mistakes.
Comment on lines +3 to +18
/**
* Tests for the cosine similarity and top-K selection logic.
* We test the exported function by mocking the NeptuneAdapter.
*/

// Helper: cosine similarity between two vectors (for verification)
function cosineSim(a: number[], b: number[]): number {
let dot = 0, normA = 0, normB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
const denom = Math.sqrt(normA) * Math.sqrt(normB);
return denom === 0 ? 0 : dot / denom;
}

Copilot AI Mar 22, 2026

Copy link

Choose a reason for hiding this comment

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

This test file only verifies a locally defined cosineSim helper and array sorting/filtering, but it does not exercise the actual exported Neptune vector search implementation (neptuneSemanticSearch) as the header comment claims. Consider importing and testing the real module (mocking NeptuneAdapter.executeQuery) so changes to production logic are covered.

Copilot uses AI. Check for mistakes.
@magyargergo

Copy link
Copy Markdown
Collaborator

Can you please ensure we are improving the coverage? We went down because there are new uncovered functionalities.

@magyargergo

Copy link
Copy Markdown
Collaborator

@claude review copilot reviews and verify their validity against the code base.

@github-actions

github-actions Bot commented Mar 22, 2026

Copy link
Copy Markdown
Contributor

Claude finished @magyargergo's task in 2m 33s —— View job


Copilot Review Validity Assessment

  • Gather context and read relevant files
  • Verify all 11 Copilot comments against current code
  • Post final summary

Verdict by Comment

✅ VALID — package.json:53 · Node engine version mismatch

The project declares "engines": { "node": ">=18.0.0" } (line 99) but "@aws-sdk/client-neptunedata": "^3.0.0" (line 53) resolves to a version that requires Node >=20 in its engines field. The ^3.0.0 range allows any 3.x version, and recent AWS SDK client packages have bumped to Node 20+. This is a real incompatibility for Node 18 users.


✅ VALID — api.ts:175 · Port not coerced + adapter.close() not awaited

Port: neptunePort comes from req.body as a raw string (e.g. "8182") and is passed directly to NeptuneDbConfig.port without parseInt(). NeptuneDbConfig.port is typed as number, so this silently passes a string at runtime.

adapter.close() not awaited: NeptuneAdapter.close() is async and returns Promise<void>. On line 174, adapter.close() is called without await in a finally block:

} finally {
  adapter.close();   // Promise unhandled
}

In practice this is low-risk since close() only calls client.destroy() (synchronous), but it's a correctness issue. Fix this →


✅ VALID — api.ts:148 · Hard-coded 500 in search/query handlers

statusFromError() is defined at line 21 and correctly used by /api/processes, /api/process, /api/clusters, and /api/cluster. But /api/search (line 148), /api/query (line 131), and /api/graph (line 116 catch block) hard-code res.status(500). A "repo not found" error returns 500 instead of 404 from these endpoints. Fix this →


⚠️ PARTIALLY VALID — analyze.ts:333 · LadybugDB embeddings block runs for Neptune

The cached-embeddings restoration block (lines 320–333) calls executeWithReusedStatement with no !isNeptune guard. However, cachedEmbeddings is only populated when initLbug() succeeds against an existing lbugPath (lines 240–251). For a first-time Neptune analyze, lbugPath won't exist, initLbug will fail, and cachedEmbeddings stays empty — so the block is a no-op.

The scenario where it matters: a user re-runs analyze on a repo previously indexed with lbug but now switching to --db neptune. In that case cachedEmbeddings would be populated, the block would run, and executeWithReusedStatement would fail silently (lbug is closed). Real bug, but low frequency.


✅ VALID — analyze.ts:346 · loadEmbeddingsToNeptune never called

Confirmed. Line 344–345 explicitly skips embeddings for Neptune:

if (isNeptune) {
  embeddingSkipReason = 'skipped (Neptune embedding support not yet available...)';
}

loadEmbeddingsToNeptune is exported from neptune-ingest.ts but never imported or called anywhere. Neptune semantic search will always return empty results. The PR description advertises this as working.


✅ VALID — analyze.ts:464 · Warning regex doesn't match Neptune format

The regex at line 463 is:

const m = w.match(/\((\d+) edges\)/);

Neptune warnings are formatted as (from neptune-ingest.ts):

  • "5 Function nodes failed to insert (2 batches skipped)" — no match
  • "12 relationships failed to insert (3 batches skipped)" — no match

totalFallback will always be 0 for Neptune repos, and the misleading "schema will be updated" message will never show even when there are real failures. Fix this →


✅ VALID — neptune-ingest.ts:233 · Cleanup deletes all nodes in a shared cluster

The cleanup query (line 228):

MATCH (n) WHERE n._gen <> $gen OR n._gen IS NULL ... DETACH DELETE n

This targets every node in the Neptune cluster that doesn't carry the current generation marker. A shared Neptune cluster used for multiple repos (or any other data) would have all its non-GitNexus nodes wiped. The previous reviewer noted this as NOT exists(n._gen) — that was fixed to IS NULL, but the cross-repo concern remains.

Note: the NOT exists()IS NULL fix is already in the current code (line 228 uses n._gen IS NULL), so that part of the original review was resolved.


✅ VALID — local-backend.ts:769 · N+1 query pattern in semanticSearch

Confirmed. After fetching top-K embedding hits via neptuneSemanticSearch, the code runs one MATCH query per result node (lines 752–755 and 809–813):

for (const embRow of rawResults) {
  // ...
  const nodeRows = await this.runParameterized(repo.id, nodeQuery, { nodeId }); // per row
}

For K=20 results, this is 20 separate HTTP round-trips to Neptune. Copilot's suggested single-UNWIND batch fetch is the correct fix. Fix this →


✅ VALID — SettingsPanel.tsx:10 · Unused getBackendUrl import

getBackendUrl is imported on line 10 but never referenced anywhere in the component body. This will fail CI if noUnusedLocals is enabled in tsconfig. Fix this →


✅ VALID — CypherConsole.tsx:50 · Missing aria-label on close button

The close button at line 50 renders only a text character with no accessible name:

<button onClick={onClose} className="text-white/40 hover:text-white transition-colors"></button>

Screen readers will announce this as "times" or just the Unicode character. Adding aria-label="Close" is the correct fix.


✅ VALID — neptune-vector-search.test.ts:18 · Tests don't exercise the real module

The test file defines its own local cosineSim function and only tests that. It never imports neptuneSemanticSearch or neptuneVectorSearch from the actual production module. Any regression in the real implementation would be invisible to this test suite.


Summary

All 11 Copilot comments are valid. The most impactful ones to address before merge:

Priority Issue Location
🔴 High loadEmbeddingsToNeptune never called — Neptune semantic search broken analyze.ts:346
🔴 High N+1 query per embedding result — will be slow/timeout local-backend.ts:769
🟡 Med Warning regex never matches Neptune format — silent failure reporting analyze.ts:463
🟡 Med Port from req.body not coerced to number api.ts:164
🟡 Med Hard-coded 500 in search/query/graph catch blocks api.ts:131,148,116
🟢 Low adapter.close() not awaited api.ts:174
🟢 Low Unused getBackendUrl import SettingsPanel.tsx:10
🟢 Low Missing aria-label on close button CypherConsole.tsx:50
🟢 Low Test doesn't cover real module neptune-vector-search.test.ts
· Branch

@magyargergo

Copy link
Copy Markdown
Collaborator

@naicud please review these findings

@magyargergo

Copy link
Copy Markdown
Collaborator

⚠️ Upcoming Prettier formatting — rebase instructions

PR #563 adds Prettier as the code formatter for the repo. When it merges, the bulk format commit will touch ~350 files (style-only: whitespace, quotes, trailing commas). Your branch will likely conflict.

After #563 merges, rebase your branch:

git fetch origin
git checkout <your-branch>
git rebase origin/main

# Conflicts will be formatting-only — accept your version:
git checkout --theirs .
git add .
git rebase --continue

# Then re-format your branch to match the new style:
npx prettier --write .
git add -A
git commit -m "style: apply prettier formatting"
git push --force-with-lease

New setup step: Run npm install at the repo root (not just in gitnexus/) to get prettier + activate the pre-commit hook. The hook auto-formats staged files on every commit going forward.

@magyargergo

Copy link
Copy Markdown
Collaborator

Please submit a new PR if this is still relevant

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants