@@ -1634,6 +1634,105 @@ TEST(tool_search_graph_accepts_project_name_alias_issue640) {
16341634 PASS ();
16351635}
16361636
1637+ /* #1025: agents pass the repo FOLDER name ("codebase-memory-mcp"), but
1638+ * indexed project names derive from the full path
1639+ * (E:\project\graph\x -> "E-project-graph-x"), so exact lookup fails with
1640+ * "project not found" while list_projects clearly shows the project. A
1641+ * passed name that matches exactly ONE indexed project as a segment-aligned
1642+ * tail ("-<name>" suffix) must resolve to it; zero or several matches keep
1643+ * the existing error. Runs against real cache-dir .db files (the resolution
1644+ * scans filenames), so this test indexes real fixtures under an overridden
1645+ * CBM_CACHE_DIR. */
1646+ static void i1025_write_repo (const char * dir , const char * fn_name ) {
1647+ char path [CBM_SZ_512 ];
1648+ snprintf (path , sizeof (path ), "%s/mod.py" , dir );
1649+ FILE * f = fopen (path , "w" );
1650+ if (!f )
1651+ return ;
1652+ fprintf (f , "def %s(x):\n return x + 1\n" , fn_name );
1653+ fclose (f );
1654+ }
1655+
1656+ TEST (tool_project_arg_resolves_unique_tail_issue1025 ) {
1657+ char repo_a [CBM_SZ_256 ];
1658+ char repo_b [CBM_SZ_256 ];
1659+ char repo_c [CBM_SZ_256 ];
1660+ char cache [CBM_SZ_256 ];
1661+ snprintf (repo_a , sizeof (repo_a ), "/tmp/cbm-i1025a-XXXXXX" );
1662+ snprintf (repo_b , sizeof (repo_b ), "/tmp/cbm-i1025b-XXXXXX" );
1663+ snprintf (repo_c , sizeof (repo_c ), "/tmp/cbm-i1025c-XXXXXX" );
1664+ snprintf (cache , sizeof (cache ), "/tmp/cbm-i1025d-XXXXXX" );
1665+ if (!cbm_mkdtemp (repo_a ) || !cbm_mkdtemp (repo_b ) || !cbm_mkdtemp (repo_c ) ||
1666+ !cbm_mkdtemp (cache )) {
1667+ FAIL ("mkdtemp failed" );
1668+ }
1669+ const char * saved_cache = getenv ("CBM_CACHE_DIR" );
1670+ char * saved_cache_copy = saved_cache ? cbm_strdup (saved_cache ) : NULL ;
1671+ cbm_setenv ("CBM_CACHE_DIR" , cache , 1 );
1672+ cbm_setenv ("CBM_INDEX_SUPERVISOR" , "0" , 1 );
1673+
1674+ i1025_write_repo (repo_a , "unique_tail_target" );
1675+ i1025_write_repo (repo_b , "amb_one" );
1676+ i1025_write_repo (repo_c , "amb_two" );
1677+
1678+ cbm_mcp_server_t * srv = cbm_mcp_server_new (NULL );
1679+ ASSERT_NOT_NULL (srv );
1680+
1681+ char args [CBM_SZ_1K ];
1682+ snprintf (args , sizeof (args ),
1683+ "{\"repo_path\":\"%s\",\"name\":\"E-project-graph-suffix1025\"}" , repo_a );
1684+ char * r = cbm_mcp_handle_tool (srv , "index_repository" , args );
1685+ ASSERT_NOT_NULL (r );
1686+ free (r );
1687+ snprintf (args , sizeof (args ), "{\"repo_path\":\"%s\",\"name\":\"F-alpha-amb1025\"}" , repo_b );
1688+ r = cbm_mcp_handle_tool (srv , "index_repository" , args );
1689+ ASSERT_NOT_NULL (r );
1690+ free (r );
1691+ snprintf (args , sizeof (args ), "{\"repo_path\":\"%s\",\"name\":\"G-beta-amb1025\"}" , repo_c );
1692+ r = cbm_mcp_handle_tool (srv , "index_repository" , args );
1693+ ASSERT_NOT_NULL (r );
1694+ free (r );
1695+
1696+ /* 1. Unique tail resolves (RED today: "project not found"). */
1697+ r = cbm_mcp_handle_tool (srv , "search_graph" ,
1698+ "{\"project\":\"suffix1025\",\"name_pattern\":\".*target.*\"}" );
1699+ ASSERT_NOT_NULL (r );
1700+ if (strstr (r , "project not found" )) {
1701+ fprintf (stderr , " [1025] FAIL unique tail did not resolve: %.200s\n" , r );
1702+ }
1703+ ASSERT_NULL (strstr (r , "project not found" ));
1704+ ASSERT_NOT_NULL (strstr (r , "unique_tail_target" ));
1705+ free (r );
1706+
1707+ /* 2. Ambiguous tail stays an error (never guess between projects). */
1708+ r = cbm_mcp_handle_tool (srv , "search_graph" ,
1709+ "{\"project\":\"amb1025\",\"name_pattern\":\".*\"}" );
1710+ ASSERT_NOT_NULL (r );
1711+ ASSERT_NOT_NULL (strstr (r , "project not found" ));
1712+ free (r );
1713+
1714+ /* 3. Exact full name keeps working unchanged. */
1715+ r = cbm_mcp_handle_tool (srv , "search_graph" ,
1716+ "{\"project\":\"E-project-graph-suffix1025\","
1717+ "\"name_pattern\":\".*target.*\"}" );
1718+ ASSERT_NOT_NULL (r );
1719+ ASSERT_NULL (strstr (r , "project not found" ));
1720+ free (r );
1721+
1722+ cbm_mcp_server_free (srv );
1723+ if (saved_cache_copy ) {
1724+ cbm_setenv ("CBM_CACHE_DIR" , saved_cache_copy , 1 );
1725+ free (saved_cache_copy );
1726+ } else {
1727+ cbm_unsetenv ("CBM_CACHE_DIR" );
1728+ }
1729+ th_rmtree (repo_a );
1730+ th_rmtree (repo_b );
1731+ th_rmtree (repo_c );
1732+ th_rmtree (cache );
1733+ PASS ();
1734+ }
1735+
16371736/* Regression for #604: path scopes architecture totals and content. */
16381737TEST (tool_get_architecture_path_scoping ) {
16391738 cbm_mcp_server_t * srv = cbm_mcp_server_new (NULL );
@@ -5679,6 +5778,7 @@ SUITE(mcp) {
56795778 RUN_TEST (tool_get_architecture_rejects_unknown_aspect_pr560 );
56805779 RUN_TEST (tool_get_architecture_accepts_project_name_alias_issue640 );
56815780 RUN_TEST (tool_search_graph_accepts_project_name_alias_issue640 );
5781+ RUN_TEST (tool_project_arg_resolves_unique_tail_issue1025 );
56825782 RUN_TEST (tool_get_architecture_path_scoping );
56835783 RUN_TEST (tool_query_graph_missing_query );
56845784
0 commit comments