Examined: Requirements (R1–R8), deploy (actions, parts), behaviour (state machine, events). Focus: error conditions, failure modes, and how the model addresses them.
| Error condition | Where it can occur | Model coverage |
|---|---|---|
| Invalid or missing path | IndexDbGraph, CleanIndex (path param) | R8: report to caller. State: indexingFailed / cleaningFailed; graph unchanged. |
| Parse failure (e.g. malformed .sysml, LSP unavailable) | Indexer during IndexDbGraph | R8: report; previous graph unchanged. indexing → indexingFailed (IndexFailed). |
| Graph store failure (Kuzu open/write/read error) | Indexer (write), McpServer (read), list/clean | R8: report. Index: indexingFailed; Clean: cleaningFailed. Query/context/impact/rename/cypher: MCP tool returns error (no new state; ready remains). |
| Invalid tool parameters (e.g. empty query, unknown symbol name) | Query, Context, Impact, Rename, Cypher, list_indexed, clean_index | R8: report to caller. Handled per call; no state change (tool returns error result). |
| Path not indexed (clean path that isn't in registry) | CleanIndex | R8: report. cleaning → cleaningFailed; graph unchanged. |
| Concurrent index/clean (e.g. two index calls overlapping) | Indexer, GraphStore | Not explicitly modeled. Implementation may serialize or reject; R8 still applies (report, no inconsistent graph). |
| Read-only tool failure (query/context/impact/rename/cypher when graph empty or symbol missing) | McpServer | No new state; tool returns error. R8: report to caller. |
| Kuzu DB lock (second process or second connection opens same DB file) | CLI and MCP, or two MCP servers | R8: report (e.g. "Could not set lock on file"). See §6 below. |
R8 ReqErrorReporting (new):
The tool SHALL report errors to the caller (MCP tool result or CLI exit/stderr) for invalid path, parse failure, graph store failure, or invalid tool parameters. It SHALL NOT leave the graph in an inconsistent state when indexing or clean fails; on index failure the previous graph content (if any) SHALL remain usable.
Satisfy: SysmledgraphDeployment; actions IndexDbGraph, CleanIndex.
New events: IndexFailed, CleanFailed.
New states:
- indexingFailed — Index failed; error reported; previous graph unchanged. Transitions: StartIndex → idle or StartIndex → indexing (retry).
- cleaningFailed — Clean failed; error reported; graph unchanged. Transitions: StartClean → ready or StartClean → cleaning (retry).
Transitions added:
- indexing → indexingFailed (accept IndexFailed)
- indexingFailed → idle (accept StartIndex)
- indexingFailed → indexing (accept StartIndex)
- cleaning → cleaningFailed (accept CleanFailed)
- cleaningFailed → ready (accept StartClean)
- cleaningFailed → cleaning (accept StartClean)
Read-only tools (query, context, impact, rename, cypher, list_indexed) do not change lifecycle state; they return success or error per call. No separate "queryFailed" state.
| Aspect | Status |
|---|---|
| R8 satisfied at deployment and at IndexDbGraph, CleanIndex | ✓ |
| State machine includes failure states and retry paths | ✓ |
| No transition from failure state leaves graph undefined (idle = empty, ready = has content) | ✓ |
| indexingFailed → idle allows "give up" and stay empty; → indexing allows retry | ✓ |
| cleaningFailed → ready preserves "graph still there"; → cleaning allows retry | ✓ |
| visualize.py (bdd) succeeds with R8 and new events/states | ✓ |
The model now addresses error conditions by:
- Requirement R8 — Report errors to caller; do not corrupt or leave graph inconsistent on index/clean failure.
- State machine — indexingFailed and cleaningFailed with explicit retry (StartIndex / StartClean) back to idle, indexing, ready, or cleaning.
- Actions — IndexDbGraph and CleanIndex document "on failure: report; previous graph unchanged" and satisfy R8.
Read-only tools are covered by R8 (report invalid params or missing data) without new states. Concurrency is left to implementation; R8 still constrains outcome (report, no inconsistent graph).
Observed behaviour: Kuzu uses file locking on the database directory/file. Only one process (or one connection per process in practice) can open the same DB path at a time. If the MCP server (e.g. Cursor) has the graph open and the user runs the CLI in a terminal, or if two MCP server instances use the same storage root, the second open fails with: "Could not set lock on file : …/db/….kuzu" (Kuzu concurrency docs).
Model alignment: GraphStore (deploy) abstracts the backing store; R3 (ReqSameStorageAsGitNexus) allows Kuzu. The model does not mandate multi-process concurrent access. A single deployment has one GraphStore; the implementation runs as either a CLI process or an MCP process, which do not share the same process.
Design constraint: For a given storage root (and thus DB path per indexed root), at most one process should hold the DB open at a time. The implementation uses a per-process cache (getCachedOrOpenGraphStore) so that within one MCP server process all tools reuse the same connection and avoid double-open; the CLI uses openGraphStore and is a separate process.
Implications:
- CLI and MCP concurrent use: If MCP is running (e.g. Cursor), CLI
analyze/list/cleanthat touch the same DB may hit the lock. Workaround: run CLI when MCP is not using the DB, or use only MCP tools (indexDbGraph, list_indexed, clean_index) when the server is running. - Read-only tools (cypher, query, context, impact): When MCP already has the DB open, these tools use the cached connection and succeed. The lock error typically appears when a second process (CLI) opens the same DB.
- R8: Lock failure is a "graph store failure"; report to caller and do not leave graph inconsistent. The implementation should surface the Kuzu error to the MCP tool result or CLI stderr.
Conclusion: The Kuzu lock is a documented limitation of the backing store (R3). The model's single GraphStore and R8 cover "store unavailable" and reporting. No new requirement is strictly necessary; the development plan and this doc should record the single-process-per-DB constraint and the CLI-vs-MCP concurrency caveat.
Deploy parts and OOP: The logical parts (Indexer, GraphStore, McpServer, Cli) map to implementation modules or classes. GraphStore is an abstraction (ports GraphWritePort, GraphReadPort, RegistryPort) with a single backing connection per DB path—implemented as interface + closure or single instance. No deep inheritance; ports represent logical interfaces. This keeps connection ownership clear: one GraphStore instance per (process, dbPath), which aligns with Kuzu's single-connection/single-process constraint.
Execution and threads: The current implementation is single-threaded (Node.js event loop). The indexer processes files sequentially (no worker threads, no parallel parse). MCP tool invocations are serialized by the event loop (one tool at a time per server instance). That avoids shared mutable state and keeps GraphStore writes on one logical thread, so the per-process cached Kuzu connection is not used from multiple threads.
Worker threads (future): If parallel indexing were added (e.g. worker threads or a thread pool to parse files concurrently), then:
- Writes to GraphStore must stay serialized (e.g. only the main thread calls addDocument/addSymbol/addEdge), or the implementation must comply with Kuzu's thread-safety (one connection per thread or documented multi-thread use). Kuzu's docs should be checked before sharing a connection across threads.
- Read-only tools (query, context, impact, cypher) could in theory run on workers only if Kuzu supports concurrent read from multiple threads; otherwise keep reads on the same thread that holds the connection.
- Model: The deploy model does not prescribe threads; it describes logical parts and ports. A design decision to introduce worker threads should be documented in the development plan and in code; the constraint "single writer / single connection ownership" (or Kuzu-compliant sharing) still applies.
Summary: OOP structure (parts = modules/classes, ports = interfaces) and single-threaded execution in the current design are consistent with the Kuzu lock and R8. Any future use of worker threads must preserve single-connection semantics or follow Kuzu's concurrency model.