Resolves the two remaining gaps from the audit.
Namespace divergence (CLI↔MCP parity):
KnowledgeGraphService persisted nodes/edges under the backend's DEFAULT
namespace — 'qe-kernel' for the CLI kernel, 'mcp-tools' for the MCP shared
backend — so a graph indexed by one tool was invisible to the other, and the
init existence check (`SELECT ... FROM kv_store WHERE namespace =
'code-intelligence:kg'`) always returned 0 because the data was elsewhere.
Now every KG kv op passes an explicit `{ namespace: 'code-intelligence:kg' }`
(via a single `nsOpts` getter so reads and writes can't drift apart — issue
#491 Bug 2). The two external readers that searched the default namespace by key
prefix (impact-analyzer test discovery, test-generator KG-context) now read that
namespace too. Verified: both `aqe code index` and the MCP `code_index` tool
land KG rows in `code-intelligence:kg`; deps/search still work; the init
`hasCodeIntelligenceIndex` check now finds the index.
FileReader stale content on incremental re-index:
indexFile() now evicts the file from the FileReader content cache before reading
it. The reader caches content for 5 min, so a re-index in a long-lived process
(the MCP server) could otherwise extract from pre-edit text. The two reads
within indexFile (entities + imports) still share one cache fill, so they stay
consistent. The incremental regression test now re-indexes on the SAME instance,
exercising both removeFileFromGraph and the cache eviction.
Adds a regression test asserting KG data is stored under 'code-intelligence:kg'
regardless of the backend's default namespace.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the gaps a devil's-advocate audit surfaced in the #511 fixes:
1. Fail-loud warning (issue request #2): `index` now records how many files
were indexed under a language with no dependency extractor and returns them
as `IndexResult.warnings` (logged + printed by the CLI), so a downstream
0-edge `deps` result is explained rather than silently misleading.
2. Incremental stale state: `indexFile(..., incremental)` now calls
removeFileFromGraph() to delete a file's prior node/entity/outgoing-edge
keys before re-indexing, so `index --incremental` prunes removed imports and
renamed entities instead of leaving phantom dependencies. (Note: within a
single long-lived process the FileReader content cache can still serve stale
text; the CLI re-indexes in a fresh process so this is not user-visible.)
3. MCP parity: the `index-code` task handler emitted `nodesCreated`/`edgesCreated`
but the MCP result mapper reads `symbolsExtracted`/`relationsFound`, so
`code_index` always reported 0/0. Map the KG counts to those fields and pass
`warnings` through. Verified via a real MCP protocol call: filesIndexed 2,
symbolsExtracted 5, relationsFound 4 (was 0/0).
Adds regression tests: unsupported-language warning, incremental stale-edge
removal (fresh-instance, mirroring the CLI), and the MCP field mapping.
Known limitation (documented, not changed here): CLI persists the graph under
the 'qe-kernel' namespace and MCP under 'mcp-tools', so a graph indexed by one
tool is not read by the other. MCP exposes only `code_index` (no deps/search),
so this has no MCP-internal reader; reconciling the namespaces is entangled with
contradictory init/test-generation expectations and needs its own change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`aqe code index` and `aqe code deps`/`search` run as separate CLI processes,
but the knowledge graph stored nodes/edges in-memory only ("cache-only"), so a
fresh `deps` process always saw an empty graph and reported 0 edges / degree-0
nodes — even though `index` reported edges created. This affected every
language; TS was incidental (the issue's "missing TS parser" theory is a red
herring — the TS Compiler API path is wired and does extract).
Fixes:
- Persist nodes/edges to memory.db in storeNode/createEdge; getEdges hydrates a
correct flat edge cache (the old source-keyed index could not serve
incoming/both lookups); clear() deletes persisted KG :node:/:edge: keys only.
- resolveImportPath(): resolve relative import specifiers to real files with
extension probing so file->file import edges connect (were dangling at
pathToNodeId('./math')).
- traverseDependencies: restrict to true dependency edge types
(import/call/extends/implements) and recurse directed-forward only, which
removes phantom A->B->A cycles surfaced once edges actually existed.
Verified via CLI: index -> 6 edges; fresh-process deps -> 2 connecting edges,
0 cycles (was 0 edges). Adds cross-instance regression tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>