Rust workspace at codeagent-engine/. Three crates: codeagent-core (lib), codeagent-cli (bin), codeagent-mcp (bin).
All 6 phases complete. Hooks, dead code detection, init command, ASP.NET Core API boundary support, and IPC retry mechanism implemented. 412 tests passing (343 core + 41 fixture + 31 MCP + 5 CLI), 2 ignored (symlink tests on Windows), 0 failures. Additionally 28 OSS integration tests pass (gated behind --features oss-tests).
- In-memory test DB:
Connection::open_in_memory()+PRAGMA foreign_keys = ON+run_migrations() - Phase 3+ test DB: also call
load_sqlite_vec()BEFOREConnection::open_in_memory(), thenensure_vec_nodes_table(&conn)after migrations make_node_for_testispub(crate) #[cfg(test)]ingraph/nodes.rsensure_root_project(&conn, Language::X)→NodeIdusable asProjectId(root_id.0)DbReaderPool::read(|conn| {...}).await— no.get()methodwriter.submit()returnsResult<()>only; smuggle data out viaArc<Mutex<T>>or write-then-readsync_fts_insertsignature:(conn, node_id, name, qualified_name, parameter_signature, return_type)— no summary arg;fts_nodeshas no summary column at schema v3NodeUpserthas noreference_countfield — that field is only onNodeRow(read-back); the SQL column defaults to 0LanguageAdapter::index_file()writes nodes/edges/spans/FTS directly to&Connection— no return struct. Callers manage transactions.- Adapter constructors:
CSharpAdapter::new(500)andTypeScriptAdapter::new(500)both takemax_signature_length: usize strip_bom()must be applied before parsing/hashing (inadapters/mod.rs)test_support.rs:#[cfg(test)] pub(crate) mod test_supportinlib.rs— providesTestRepoBuilder(temp dir + optional git init) andTestDb(file-backed SQLite with writer thread + reader pool for#[tokio::test]pipeline tests)- IPC
from_streams()test helper:ipc::process::testing::from_streams(writer, reader, deadline_ms)— buildsLanguageServiceProcessfromDuplexStreampairs instead of spawning child processes. Tests usetokio::io::duplex()pairs and a mock server task. insert_span()takes&SpanUpsertstruct — not individual positional arguments- Symlink tests: gated with
#[cfg_attr(windows, ignore)]— symlink creation requires elevated privileges on Windows detect_symbol_renames,apply_symbol_rename,apply_file_rename,SymbolRenameMatcharepub(crate)iningest/rename.rsfor test access- IPC retry mechanism:
IpcManager::run_analysis()wrapsrun_analysis_once()in an exponential backoff loop. Retryable:IpcChildExited,IpcTimeout, OOM restart (-32104). Non-retryable:IpcVersionMismatch,Cancelled, deterministic semantic errors (-32100/-32101/-32102). Config:ipc_max_retries(default 2),ipc_retry_base_delay_ms(default 500ms).CoreError::is_retryable_ipc()classifies errors.
- C#:
qualified_name:node_type[:param_count(types)]— e.g.MyApp.Auth.Login:method:1(string) - TypeScript: all function declarations stored as
NodeType::Method(notNodeType::Function) - Shared:
derive_file_id(path)inadapters/mod.rs— deterministic SHA-256 FileId, used by both adapters
- C#
handle_namespacedouble-naming — Fixed: compute qname before push. deletion_logmissingparameter_count— Fixed: useNonedirectly.DeletionJournalEntrymissingchunk_fingerprint— Fixed ingraph/deletion.rs.upsert_edgeINSERT OR IGNORE no-op — Fixed: replaced withINSERT … WHERE NOT EXISTS.replace_semantic_edges_in_txapproximate→exact upgrade — Fixed: DELETE targetsconfidence IN ('exact', 'approximate').filter_nodes_ftsMATCH alias — Fixed:WHERE fts_nodes MATCH ?1(table name, not alias).upsert_nodedoes not auto-populatefts_nodes— Callers must callsync_fts_insertseparately.sqlite-vec v0.1.6has noload()function — Fixed:load_sqlite_vec()usessqlite3_auto_extension.vec0rejects BLOB(16) PRIMARY KEY — Decision: regularvec_nodestable with brute-force cosine.- C#
FileId::new()non-deterministic — Fixed:derive_file_id(path)(SHA-256 based). - C# namespace span ownership — Fixed: fallback to non-primary span.
- TS interface body not walked — Fixed: extended
walk_class_body. - FTS5 dot-qualified names cause syntax error — Fixed: wrap in double quotes in
build_fts_query().
- Migration 001:
nodes,edges,node_spans,deletion_log,fts_nodes - Migration 002:
node_identity_map,idx_node_identity_map_old - Migration 003: rebuild
fts_nodeswithoutsummarycolumn; removesummary_prompt_versionmetadata - Migration 004:
api_endpointstable with indexes onnode_id,controller_id,http_method,route_template CURRENT_SCHEMA_VERSION = 4vec_nodestable: NOT in migration SQL; created byensure_vec_nodes_table(conn)separately- No schema migration for Phase 4, 5, or 6
embedding/provider.rs:EmbeddingProvidertrait;HashEmbeddingProviderfor testsgraph/vectors.rs:ensure_vec_nodes_table,insert_or_replace_embedding,delete_embedding,handle_model_change,vector_search_brute_forcequery/mod.rs: All SELECT queries select 29 columns (0–28); edge columns at 29/30/31; FTS rank at 29db/connection.rs:load_sqlite_vec()(no-arg) registers global auto-extension
retrieval/: intent classification, multi-channel search (vector/BM25/qname), RRF merge+rerank, context assemblyeval/: NDCG, MRR, precision, recall metrics; dataset loader; 100 curated eval queries
- Crate:
crates/codeagent-mcp— binary crate,rmcp v0.16SDK state.rs:CodeagentServicestruct with#[tool_router](19 tools) +#[tool_handler]forServerHandlererror.rs:CoreError→ MCP error codes;ErrorCodewrapsi32(not i64)sandbox.rs:resolve_sandboxed(repo_root, relative_path)— rejects.., canonicalizes, checksstarts_withserialization.rs:node_to_json,filter_result_to_json,neighbor_to_json,span_to_json,outline_node_to_json,similar_node_to_json,api_endpoint_to_json,api_endpoint_with_node_to_jsontools/filesystem.rs:list_directory,read_file,get_directory_treetools/search.rs:search_symbols(FTS),lookup_symbol(qname),find_similar(embedding+brute-force cosine),search_api_endpoints(HTTP method/route/controller filter)tools/navigation.rs: 10 tools:get_symbol,get_source_spans,get_file_outline,get_callers,get_callees,get_implementations,get_references,get_dependencies,get_dependents,find_dead_codeget_symbolattachesapi_endpointfield when the node has endpoint dataget_file_outlineattachesapi_endpointto action methods using batchget_api_endpoints_for_file+ HashMap lookup
tools/management.rs:index_files(pipeline batch),get_status(DB stats)- Key rmcp patterns:
Parameters<T>for tool params (T:Deserialize + schemars::JsonSchema),schemars = "1.0" ChangeBatchhas nofrom_created_paths— pushFileChange::Createdmanually
adapters/mod.rs:strip_bom()— strips UTF-8 BOM (EF BB BF) before parsing/hashingquery/mod.rs:get_transitive_neighbors()—WITH RECURSIVECTE; usesUNION(notUNION ALL) onnode_idonlyconfig.rs:LoggingConfig { debug_content_logging: bool }— defaultfalse- Key lesson:
WITH RECURSIVE+UNIONdeduplicates on ALL CTE columns — CTE overnode_idonly for cycle termination
compute_pagerank(conn, opts) -> Result<Vec<(NodeId, f64)>>— loads edges into memory, iterative power methodget_pagerank_summary(conn, opts) -> Result<Vec<(Node, f64)>>— full Node metadata for ranked resultsPageRankOptions { edge_types, damping, iterations, top_n }with sensible defaults
find_dead_code(conn, opts) -> Result<Vec<DeadCodeEntry>>— single SQL query, excludes constructors/overrides/public API by defaultcount_dead_code(conn, opts) -> Result<usize>— lightweight count variantDeadCodeOptions { project_id, language, include_public_api, limit }— public API excluded by defaultDeadCodeEntry { node: Node }— wraps full Node metadata
FindDeadCodeParams { project_id, language, include_public_api, limit }— default limit 50- Wired into
CodeagentServicetool_router instate.rs
- PreCompact: PageRank top-30 symbols as Markdown table + graph stats →
additionalContext - PostToolUse: Extracts
file_pathfromtool_input, re-indexes single file via adapterindex_file()(syntactic-only, WAL + busy_timeout) - SubagentStart: Project overview with top-15 symbols + available MCP tool guidance →
additionalContext - TaskCompleted: Dead code report + unresolved reference count →
additionalContext - Shared:
read_hook_input()(stdin JSON),write_hook_output()(stdout JSON),open_db_for_hook()(WAL + busy_timeout=5000 + migrations) - Repo root from
$CLAUDE_PROJECT_DIRenv var →cwdfield → current directory fallback
codeagent init [--repo-root <path>]— one-command project setup- Creates
.codeagent/dir,config.json,index.db(with migrations),.gitignoreentry - Merges hook registrations into
.claude/settings.json(preserves existing settings) - 4 hooks registered: PreCompact, PostToolUse (matcher: Edit|Write|NotebookEdit), SubagentStart, TaskCompleted
- Subcommands:
init,hook <event>,get-node,get-neighbors,get-source,get-outline,filter,lookup,health hookevents:pre-compact,post-tool-use,subagent-start,task-completed- Init and Hook commands bypass the global
--dbflag (they resolve paths independently)
- File:
crates/codeagent-core/tests/oss_tests.rs— gated behindcargo test --features oss-tests - Repos: tRPC (TS, v11.10.0, MIT) and Hot Chocolate graphql-platform (C#, 15.1.12, MIT)
- 28 tests across 4 tiers (baseline, navigation, complex queries, benchmarks)
graph/api_endpoints.rs:ApiStyleenum (Controller/MinimalApi/Grpc),ApiEndpointUpsert,ApiEndpointstructs; CRUD:upsert_api_endpoint,delete_api_endpoints_for_file,get_api_endpoint,get_api_endpoints_for_file,search_api_endpointsapi_endpointstable: separate fromnodes(no 29-column bloat);ON DELETE CASCADEonnode_id; indexes onnode_id,controller_id,http_method,route_template- Tree-sitter extraction:
AspNetControllerContexttracks controller state while walking class body;extract_aspnet_attributes()parses[ApiController],[HttpGet],[Route],[Authorize],[AllowAnonymous],[ProducesResponseType],[Consumes],[FromBody] - Route resolution:
resolve_route_template(class_route, method_route, controller_name)— replaces[controller]token, combines class + method routes with/; absolute method routes (starting with/) override class route - Tree-sitter C# field name: method return types use
child_by_field_name("returns")NOT"type"—"type"is for properties/events - Controller detection:
[ApiController]attribute ORControllerBase/Controllerin base list — both trigger endpoint extraction - IPC extension:
ApiEndpointInfostruct onSemanticNodewithskip_serializing_if = "Option::is_none"for backward compat - Roslyn extractor:
ExtractApiEndpoint(IMethodSymbol)usesGetAttributes()for authoritative data; overwrites tree-sitter approximation withextractor_version = "roslyn" - Semantic enrichment:
process_enrichment_result()processesapi_endpointfromSemanticNode;lookup_controller_id()finds parent class viaContainsedge - MCP tool:
search_api_endpoints— params:http_method,route_pattern,controller_id,limit; returns endpoint metadata + method node metadata - MCP enrichment:
get_symbolattachesapi_endpointwhen present;get_file_outlinebatch-loads endpoints viaget_api_endpoints_for_file+ HashMap
- Pipeline integration tests (
ingest/pipeline.rs) — requires tokio, IpcManager stubs - T-275: deferred test requiring file-based DB + async writer for WAL isolation