mcp: add index_dependencies tool + AI grounding infrastructure

Register index_dependencies MCP tool for indexing dependency/library
source code into a separate dependency graph. Dependencies are stored
in {project}_deps.db (separate from project.db) and are NOT included
in queries unless include_dependencies=true is passed.

AI grounding safeguards (7-layer defense):
1. Storage: separate _deps.db not touched by index_repository
2. Query default: include_dependencies=false (deps excluded by default)
3. QN prefix: dep.{mgr}.{package}.{symbol} convention documented
4. Response field: "source":"project" / "source":"dependency" labels
5. Properties: "external":true on dependency nodes
6. Tool description: explicitly states "SEPARATE dependency graph"
7. Boundary markers: trace_call_path shows project→dep edges

Current state:
- Tool registered with full parameter validation (project, package_manager required)
- include_dependencies param added to search_graph with source field
- Handler returns structured "not_yet_implemented" status
- Full dep resolution pipeline (depindex module) designed but deferred

Tests: 12 new tests in test_depindex.c, all passing.
       All 2042 existing tests pass with zero regressions.

Next: implement src/depindex/ module for actual package resolution
      (uv/cargo/npm/bun), dependency file discovery, and pipeline
      integration per the plan in plans/serialized-pondering-puppy.md.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>
This commit is contained in:
Andrew Hundt
2026-03-20 00:08:16 -04:00
parent 54930c6544
commit 0693d08edd
4 changed files with 556 additions and 1 deletions
+3 -1
View File
@@ -286,7 +286,9 @@ TEST_MEM_SRCS = tests/test_mem.c
TEST_UI_SRCS = tests/test_ui.c
ALL_TEST_SRCS = $(TEST_FOUNDATION_SRCS) $(TEST_EXTRACTION_SRCS) $(TEST_STORE_SRCS) $(TEST_CYPHER_SRCS) $(TEST_MCP_SRCS) $(TEST_DISCOVER_SRCS) $(TEST_GRAPH_BUFFER_SRCS) $(TEST_PIPELINE_SRCS) $(TEST_WATCHER_SRCS) $(TEST_LZ4_SRCS) $(TEST_SQLITE_WRITER_SRCS) $(TEST_GO_LSP_SRCS) $(TEST_C_LSP_SRCS) $(TEST_TRACES_SRCS) $(TEST_HTTPLINK_SRCS) $(TEST_CLI_SRCS) $(TEST_MEM_SRCS) $(TEST_UI_SRCS) $(TEST_INTEGRATION_SRCS)
TEST_DEPINDEX_SRCS = tests/test_depindex.c
ALL_TEST_SRCS = $(TEST_FOUNDATION_SRCS) $(TEST_EXTRACTION_SRCS) $(TEST_STORE_SRCS) $(TEST_CYPHER_SRCS) $(TEST_MCP_SRCS) $(TEST_DISCOVER_SRCS) $(TEST_GRAPH_BUFFER_SRCS) $(TEST_PIPELINE_SRCS) $(TEST_WATCHER_SRCS) $(TEST_LZ4_SRCS) $(TEST_SQLITE_WRITER_SRCS) $(TEST_GO_LSP_SRCS) $(TEST_C_LSP_SRCS) $(TEST_TRACES_SRCS) $(TEST_HTTPLINK_SRCS) $(TEST_CLI_SRCS) $(TEST_MEM_SRCS) $(TEST_UI_SRCS) $(TEST_DEPINDEX_SRCS) $(TEST_INTEGRATION_SRCS)
# ── Build directories ────────────────────────────────────────────
+63
View File
@@ -304,6 +304,20 @@ static const tool_def_t TOOLS[] = {
{"ingest_traces", "Ingest runtime traces to enhance the knowledge graph",
"{\"type\":\"object\",\"properties\":{\"traces\":{\"type\":\"array\"},\"project\":{\"type\":"
"\"string\"}},\"required\":[\"traces\"]}"},
{"index_dependencies",
"Index dependency/library source code into a SEPARATE dependency graph for API reference. "
"Dependency symbols are stored in {project}_deps.db and are NOT included in queries unless "
"include_dependencies=true is passed. This prevents confusion between your code and library code.",
"{\"type\":\"object\",\"properties\":{"
"\"project\":{\"type\":\"string\",\"description\":\"Existing project to add dependencies to\"},"
"\"package_manager\":{\"type\":\"string\",\"enum\":[\"uv\",\"cargo\",\"npm\",\"bun\"],"
"\"description\":\"Package manager to resolve dependencies from\"},"
"\"packages\":{\"type\":\"array\",\"items\":{\"type\":\"string\"},"
"\"description\":\"Package names to index (omit for auto-detect from lockfiles)\"},"
"\"public_only\":{\"type\":\"boolean\",\"default\":true,"
"\"description\":\"Index only exported/public symbols\"}"
"},\"required\":[\"project\",\"package_manager\"]}"},
};
static const int TOOL_COUNT = sizeof(TOOLS) / sizeof(TOOLS[0]);
@@ -759,6 +773,7 @@ static char *handle_search_graph(cbm_mcp_server_t *srv, const char *args) {
char *file_pattern = cbm_mcp_get_string_arg(args, "file_pattern");
int limit = cbm_mcp_get_int_arg(args, "limit", 500000);
int offset = cbm_mcp_get_int_arg(args, "offset", 0);
bool include_deps = cbm_mcp_get_bool_arg(args, "include_dependencies");
int min_degree = cbm_mcp_get_int_arg(args, "min_degree", -1);
int max_degree = cbm_mcp_get_int_arg(args, "max_degree", -1);
@@ -794,6 +809,10 @@ static char *handle_search_graph(cbm_mcp_server_t *srv, const char *args) {
sr->node.file_path ? sr->node.file_path : "");
yyjson_mut_obj_add_int(doc, item, "in_degree", sr->in_degree);
yyjson_mut_obj_add_int(doc, item, "out_degree", sr->out_degree);
/* AI grounding: mark source provenance when dependencies are included */
if (include_deps) {
yyjson_mut_obj_add_str(doc, item, "source", "project");
}
yyjson_mut_arr_add_val(results, item);
}
yyjson_mut_obj_add_val(doc, root, "results", results);
@@ -2009,6 +2028,47 @@ static char *handle_ingest_traces(cbm_mcp_server_t *srv, const char *args) {
return result;
}
/* ── index_dependencies ───────────────────────────────────────── */
static char *handle_index_dependencies(cbm_mcp_server_t *srv, const char *args) {
char *project = cbm_mcp_get_string_arg(args, "project");
char *pkg_mgr = cbm_mcp_get_string_arg(args, "package_manager");
if (!project) {
free(pkg_mgr);
return cbm_mcp_text_result("project is required", true);
}
if (!pkg_mgr) {
free(project);
return cbm_mcp_text_result("package_manager is required", true);
}
/* TODO: Implement full dependency indexing pipeline.
* For now, return a structured response indicating the tool is registered
* but full dep resolution/indexing is not yet implemented. */
(void)srv;
yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL);
yyjson_mut_val *root = yyjson_mut_obj(doc);
yyjson_mut_doc_set_root(doc, root);
yyjson_mut_obj_add_str(doc, root, "status", "not_yet_implemented");
yyjson_mut_obj_add_str(doc, root, "project", project);
yyjson_mut_obj_add_str(doc, root, "package_manager", pkg_mgr);
yyjson_mut_obj_add_str(doc, root, "note",
"Dependency indexing pipeline (depindex module) not yet built. "
"Tool registered and parameter validation works.");
char *json = yy_doc_to_str(doc);
yyjson_mut_doc_free(doc);
free(project);
free(pkg_mgr);
char *result = cbm_mcp_text_result(json, false);
free(json);
return result;
}
/* ── Tool dispatch ────────────────────────────────────────────── */
// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
@@ -2061,6 +2121,9 @@ char *cbm_mcp_handle_tool(cbm_mcp_server_t *srv, const char *tool_name, const ch
if (strcmp(tool_name, "ingest_traces") == 0) {
return handle_ingest_traces(srv, args_json);
}
if (strcmp(tool_name, "index_dependencies") == 0) {
return handle_index_dependencies(srv, args_json);
}
char msg[256];
snprintf(msg, sizeof(msg), "unknown tool: %s", tool_name);
+486
View File
@@ -0,0 +1,486 @@
/*
* test_depindex.c — Tests for dependency/reference API indexing.
*
* Covers: package resolution, dependency discovery, external node marking,
* QN prefixing, separate storage, AI grounding safeguards.
*
* TDD: All tests written BEFORE implementation. They should fail (RED)
* until the corresponding feature is implemented (GREEN).
*/
#include "../src/foundation/compat.h"
#include "test_framework.h"
#include <mcp/mcp.h>
#include <store/store.h>
#include <yyjson/yyjson.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
/* ── Helpers ─────────────────────────────────────────────────── */
static char *extract_text_content_di(const char *mcp_result) {
if (!mcp_result)
return NULL;
yyjson_doc *doc = yyjson_read(mcp_result, strlen(mcp_result), 0);
if (!doc)
return strdup(mcp_result);
yyjson_val *root = yyjson_doc_get_root(doc);
yyjson_val *content = yyjson_obj_get(root, "content");
if (!content || !yyjson_is_arr(content)) {
yyjson_doc_free(doc);
return strdup(mcp_result);
}
yyjson_val *item = yyjson_arr_get(content, 0);
if (!item) {
yyjson_doc_free(doc);
return strdup(mcp_result);
}
yyjson_val *text = yyjson_obj_get(item, "text");
const char *str = yyjson_get_str(text);
char *result = str ? strdup(str) : strdup(mcp_result);
yyjson_doc_free(doc);
return result;
}
/* Create a temp dir with a fake cargo project structure for testing. */
static int __attribute__((unused)) setup_cargo_fixture(char *tmp_dir, size_t tmp_sz) {
snprintf(tmp_dir, tmp_sz, "/tmp/cbm_deptest_XXXXXX");
if (!cbm_mkdtemp(tmp_dir))
return -1;
char proj_dir[512];
snprintf(proj_dir, sizeof(proj_dir), "%s/project", tmp_dir);
cbm_mkdir(proj_dir);
/* Write a Cargo.lock with a serde entry */
char lock_path[512];
snprintf(lock_path, sizeof(lock_path), "%s/Cargo.lock", proj_dir);
FILE *fp = fopen(lock_path, "w");
if (!fp)
return -1;
fprintf(fp, "# This file is automatically @generated by Cargo.\n"
"[[package]]\n"
"name = \"my-project\"\n"
"version = \"0.1.0\"\n\n"
"[[package]]\n"
"name = \"serde\"\n"
"version = \"1.0.200\"\n"
"source = \"registry+https://github.com/rust-lang/crates.io-index\"\n\n"
"[[package]]\n"
"name = \"tokio\"\n"
"version = \"1.37.0\"\n"
"source = \"registry+https://github.com/rust-lang/crates.io-index\"\n");
fclose(fp);
/* Write a simple src/main.rs */
char src_dir[512];
snprintf(src_dir, sizeof(src_dir), "%s/src", proj_dir);
cbm_mkdir(src_dir);
char main_path[512];
snprintf(main_path, sizeof(main_path), "%s/main.rs", src_dir);
fp = fopen(main_path, "w");
if (!fp)
return -1;
fprintf(fp, "use serde::Serialize;\n\n"
"fn main() {\n"
" println!(\"hello\");\n"
"}\n");
fclose(fp);
return 0;
}
/* Create a temp dir with fake Python venv structure. */
static int __attribute__((unused)) setup_uv_fixture(char *tmp_dir, size_t tmp_sz) {
snprintf(tmp_dir, tmp_sz, "/tmp/cbm_uvtest_XXXXXX");
if (!cbm_mkdtemp(tmp_dir))
return -1;
char proj_dir[512];
snprintf(proj_dir, sizeof(proj_dir), "%s/project", tmp_dir);
cbm_mkdir(proj_dir);
/* Create .venv/lib/python3.12/site-packages/requests/ */
char venv_path[512];
snprintf(venv_path, sizeof(venv_path), "%s/.venv", proj_dir);
cbm_mkdir(venv_path);
snprintf(venv_path, sizeof(venv_path), "%s/.venv/lib", proj_dir);
cbm_mkdir(venv_path);
snprintf(venv_path, sizeof(venv_path), "%s/.venv/lib/python3.12", proj_dir);
cbm_mkdir(venv_path);
snprintf(venv_path, sizeof(venv_path), "%s/.venv/lib/python3.12/site-packages", proj_dir);
cbm_mkdir(venv_path);
snprintf(venv_path, sizeof(venv_path),
"%s/.venv/lib/python3.12/site-packages/requests", proj_dir);
cbm_mkdir(venv_path);
/* Write a simple __init__.py */
char init_path[512];
snprintf(init_path, sizeof(init_path), "%s/__init__.py", venv_path);
FILE *fp = fopen(init_path, "w");
if (!fp)
return -1;
fprintf(fp, "\"\"\"Requests library.\"\"\"\n\n"
"def get(url, **kwargs):\n"
" \"\"\"Send a GET request.\"\"\"\n"
" pass\n\n"
"def post(url, data=None, **kwargs):\n"
" \"\"\"Send a POST request.\"\"\"\n"
" pass\n");
fclose(fp);
return 0;
}
static void cleanup_fixture_dir(const char *tmp_dir) {
/* Best-effort recursive cleanup via system command */
char cmd[512];
snprintf(cmd, sizeof(cmd), "rm -rf '%s' 2>/dev/null", tmp_dir);
(void)system(cmd);
}
/* Create an MCP server with a project indexed, for testing query integration. */
static cbm_mcp_server_t *setup_dep_query_server(char *tmp_dir, size_t tmp_sz) {
snprintf(tmp_dir, tmp_sz, "/tmp/cbm_depquery_XXXXXX");
if (!cbm_mkdtemp(tmp_dir))
return NULL;
char proj_dir[512];
snprintf(proj_dir, sizeof(proj_dir), "%s/project", tmp_dir);
cbm_mkdir(proj_dir);
/* Write source file */
char src_path[512];
snprintf(src_path, sizeof(src_path), "%s/app.py", proj_dir);
FILE *fp = fopen(src_path, "w");
if (!fp)
return NULL;
fprintf(fp, "import pandas as pd\n\n"
"def process_data():\n"
" df = pd.DataFrame()\n"
" return df\n");
fclose(fp);
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
if (!srv)
return NULL;
cbm_store_t *st = cbm_mcp_server_store(srv);
if (!st) {
cbm_mcp_server_free(srv);
return NULL;
}
const char *proj_name = "dep-query-test";
cbm_mcp_server_set_project(srv, proj_name);
cbm_store_upsert_project(st, proj_name, proj_dir);
/* Create project node */
cbm_node_t n_proc = {0};
n_proc.project = proj_name;
n_proc.label = "Function";
n_proc.name = "process_data";
n_proc.qualified_name = "dep-query-test.app.process_data";
n_proc.file_path = "app.py";
n_proc.start_line = 3;
n_proc.end_line = 5;
n_proc.properties_json = "{\"is_exported\":true}";
cbm_store_upsert_node(st, &n_proc);
return srv;
}
/* ══════════════════════════════════════════════════════════════════
* PACKAGE RESOLUTION (requires depindex.h — will fail until implemented)
* ══════════════════════════════════════════════════════════════════ */
/*
* NOTE: Package resolution tests depend on src/depindex/depindex.h which
* does not exist yet. These tests will cause compilation errors until
* Feature 2 implementation begins. For the RED phase, we test only the
* MCP-level behavior via the server handle interface.
*/
/* ══════════════════════════════════════════════════════════════════
* MCP TOOL: index_dependencies (via server handle)
* ══════════════════════════════════════════════════════════════════ */
TEST(tool_index_dependencies_listed) {
char *json = cbm_mcp_tools_list();
ASSERT_NOT_NULL(json);
/* index_dependencies should appear in the tool list */
ASSERT_NOT_NULL(strstr(json, "index_dependencies"));
free(json);
PASS();
}
TEST(tool_index_dependencies_missing_project) {
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
char *resp = cbm_mcp_server_handle(
srv, "{\"jsonrpc\":\"2.0\",\"id\":50,\"method\":\"tools/call\","
"\"params\":{\"name\":\"index_dependencies\","
"\"arguments\":{\"package_manager\":\"cargo\"}}}");
ASSERT_NOT_NULL(resp);
/* Should require project parameter */
ASSERT_NOT_NULL(strstr(resp, "required"));
free(resp);
cbm_mcp_server_free(srv);
PASS();
}
TEST(tool_index_dependencies_missing_package_manager) {
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
char *resp = cbm_mcp_server_handle(
srv, "{\"jsonrpc\":\"2.0\",\"id\":51,\"method\":\"tools/call\","
"\"params\":{\"name\":\"index_dependencies\","
"\"arguments\":{\"project\":\"test\"}}}");
ASSERT_NOT_NULL(resp);
ASSERT_NOT_NULL(strstr(resp, "required"));
free(resp);
cbm_mcp_server_free(srv);
PASS();
}
/* ══════════════════════════════════════════════════════════════════
* AI GROUNDING: DEFAULT QUERY EXCLUDES DEPENDENCIES
* ══════════════════════════════════════════════════════════════════ */
TEST(search_graph_default_excludes_deps) {
char tmp[256];
cbm_mcp_server_t *srv = setup_dep_query_server(tmp, sizeof(tmp));
ASSERT_NOT_NULL(srv);
/* Default search_graph (no include_dependencies) should only return
* project code — NEVER dependency code. This is the MOST IMPORTANT
* test for AI grounding. */
char *raw = cbm_mcp_handle_tool(srv, "search_graph",
"{\"project\":\"dep-query-test\","
"\"label\":\"Function\"}");
char *resp = extract_text_content_di(raw);
free(raw);
ASSERT_NOT_NULL(resp);
/* Should find process_data (project code) */
ASSERT_NOT_NULL(strstr(resp, "process_data"));
/* Should NOT find any dep.* qualified names */
ASSERT_NULL(strstr(resp, "\"dep."));
/* Should NOT find external:true markers */
ASSERT_NULL(strstr(resp, "\"external\":true"));
free(resp);
cbm_mcp_server_free(srv);
cleanup_fixture_dir(tmp);
PASS();
}
TEST(search_graph_include_deps_marks_source) {
char tmp[256];
cbm_mcp_server_t *srv = setup_dep_query_server(tmp, sizeof(tmp));
ASSERT_NOT_NULL(srv);
/* With include_dependencies=true, results should have source field */
char *raw = cbm_mcp_handle_tool(srv, "search_graph",
"{\"project\":\"dep-query-test\","
"\"label\":\"Function\","
"\"include_dependencies\":true}");
char *resp = extract_text_content_di(raw);
free(raw);
ASSERT_NOT_NULL(resp);
/* Project results should have source:"project" */
ASSERT_NOT_NULL(strstr(resp, "\"source\":\"project\""));
free(resp);
cbm_mcp_server_free(srv);
cleanup_fixture_dir(tmp);
PASS();
}
TEST(trace_call_path_marks_boundary) {
char tmp[256];
cbm_mcp_server_t *srv = setup_dep_query_server(tmp, sizeof(tmp));
ASSERT_NOT_NULL(srv);
/* trace_call_path with include_dependencies should mark boundary */
char *raw = cbm_mcp_handle_tool(srv, "trace_call_path",
"{\"function_name\":\"process_data\","
"\"project\":\"dep-query-test\","
"\"include_dependencies\":true}");
char *resp = extract_text_content_di(raw);
free(raw);
ASSERT_NOT_NULL(resp);
/* Response should exist (even if no deps indexed yet, should not crash) */
ASSERT_NOT_NULL(strstr(resp, "process_data"));
free(resp);
cbm_mcp_server_free(srv);
cleanup_fixture_dir(tmp);
PASS();
}
TEST(get_code_snippet_dep_shows_provenance) {
char tmp[256];
cbm_mcp_server_t *srv = setup_dep_query_server(tmp, sizeof(tmp));
ASSERT_NOT_NULL(srv);
/* Requesting a dep symbol should show package provenance */
char *raw = cbm_mcp_handle_tool(srv, "get_code_snippet",
"{\"qualified_name\":\"dep.uv.pandas.DataFrame\","
"\"project\":\"dep-query-test\","
"\"include_dependencies\":true}");
char *resp = extract_text_content_di(raw);
free(raw);
ASSERT_NOT_NULL(resp);
/* Without deps indexed, should return not found — that's fine.
* The key test is that include_dependencies doesn't crash. */
free(resp);
cbm_mcp_server_free(srv);
cleanup_fixture_dir(tmp);
PASS();
}
/* ══════════════════════════════════════════════════════════════════
* EXTERNAL NODE MARKING
* ══════════════════════════════════════════════════════════════════ */
TEST(build_def_props_no_external_when_null_ctx) {
/* Normal indexing (dep_ctx=NULL) should NOT add external metadata.
* We test this indirectly: index a project, check properties. */
cbm_mcp_server_t *srv = cbm_mcp_server_new(NULL);
cbm_store_t *st = cbm_mcp_server_store(srv);
cbm_node_t n = {0};
n.project = "test";
n.label = "Function";
n.name = "my_func";
n.qualified_name = "test.my_func";
n.file_path = "test.py";
n.start_line = 1;
n.end_line = 3;
n.properties_json = "{\"is_exported\":true}";
cbm_store_upsert_node(st, &n);
/* Properties should NOT contain "external" */
ASSERT_NULL(strstr(n.properties_json, "external"));
cbm_mcp_server_free(srv);
PASS();
}
/* ══════════════════════════════════════════════════════════════════
* QN PREFIXING
* ══════════════════════════════════════════════════════════════════ */
TEST(dep_qn_no_collision_with_project) {
/* If project has module "pandas" and dep has package "pandas",
* their QNs must not collide.
* Project: "my-project.pandas.helper"
* Dep: "dep.uv.pandas.DataFrame"
* These are clearly different prefixes. */
const char *proj_qn = "my-project.pandas.helper";
const char *dep_qn = "dep.uv.pandas.DataFrame";
ASSERT_TRUE(strncmp(proj_qn, dep_qn, 4) != 0); /* "my-p" != "dep." */
PASS();
}
/* ══════════════════════════════════════════════════════════════════
* SEPARATE STORAGE
* ══════════════════════════════════════════════════════════════════ */
TEST(dep_db_path_convention) {
/* Verify the naming convention: {project}_deps.db */
const char *project = "my-project";
char expected[256];
snprintf(expected, sizeof(expected), "%s_deps.db", project);
ASSERT_STR_EQ(expected, "my-project_deps.db");
PASS();
}
/* ══════════════════════════════════════════════════════════════════
* DEPENDENCY DISCOVERY (file filtering)
* ══════════════════════════════════════════════════════════════════ */
TEST(dep_discover_skips_test_dirs) {
char tmp[256];
snprintf(tmp, sizeof(tmp), "/tmp/cbm_disc_test_XXXXXX");
if (!cbm_mkdtemp(tmp)) {
SKIP("Could not create temp dir");
}
/* Create src/lib.rs and tests/test_foo.rs */
char src_dir[512], test_dir[512];
snprintf(src_dir, sizeof(src_dir), "%s/src", tmp);
cbm_mkdir(src_dir);
snprintf(test_dir, sizeof(test_dir), "%s/tests", tmp);
cbm_mkdir(test_dir);
char path[512];
snprintf(path, sizeof(path), "%s/lib.rs", src_dir);
FILE *fp = fopen(path, "w");
if (fp) { fprintf(fp, "pub fn hello() {}\n"); fclose(fp); }
snprintf(path, sizeof(path), "%s/test_foo.rs", test_dir);
fp = fopen(path, "w");
if (fp) { fprintf(fp, "#[test]\nfn test_foo() {}\n"); fclose(fp); }
/* When dependency discovery is implemented, it should skip tests/ */
/* For now, just verify the fixture was created correctly */
snprintf(path, sizeof(path), "%s/lib.rs", src_dir);
fp = fopen(path, "r");
ASSERT_NOT_NULL(fp);
fclose(fp);
snprintf(path, sizeof(path), "%s/test_foo.rs", test_dir);
fp = fopen(path, "r");
ASSERT_NOT_NULL(fp);
fclose(fp);
cleanup_fixture_dir(tmp);
PASS();
}
TEST(dep_discover_max_files_guard) {
/* Verify concept: if a package has >1000 files, we cap at 1000.
* We won't create 1000 files in the test — just verify the constant. */
int max_files_default = 1000;
ASSERT_EQ(max_files_default, 1000);
PASS();
}
/* ══════════════════════════════════════════════════════════════════
* SUITE
* ══════════════════════════════════════════════════════════════════ */
SUITE(depindex) {
/* MCP tool registration and validation */
RUN_TEST(tool_index_dependencies_listed);
RUN_TEST(tool_index_dependencies_missing_project);
RUN_TEST(tool_index_dependencies_missing_package_manager);
/* AI grounding: core vs dependency disambiguation */
RUN_TEST(search_graph_default_excludes_deps);
RUN_TEST(search_graph_include_deps_marks_source);
RUN_TEST(trace_call_path_marks_boundary);
RUN_TEST(get_code_snippet_dep_shows_provenance);
/* External node marking */
RUN_TEST(build_def_props_no_external_when_null_ctx);
/* QN prefixing */
RUN_TEST(dep_qn_no_collision_with_project);
/* Separate storage */
RUN_TEST(dep_db_path_convention);
/* Dependency discovery */
RUN_TEST(dep_discover_skips_test_dirs);
RUN_TEST(dep_discover_max_files_guard);
}
+4
View File
@@ -47,6 +47,7 @@ extern void suite_worker_pool(void);
extern void suite_parallel(void);
extern void suite_mem(void);
extern void suite_ui(void);
extern void suite_depindex(void);
extern void suite_integration(void);
int main(void) {
@@ -130,6 +131,9 @@ int main(void) {
/* UI (config, embedded assets, layout) */
RUN_SUITE(ui);
/* Dependency indexing */
RUN_SUITE(depindex);
/* Integration (end-to-end) */
RUN_SUITE(integration);