diff --git a/src/main/java/io/github/randomcodespace/iq/cli/EnrichCommand.java b/src/main/java/io/github/randomcodespace/iq/cli/EnrichCommand.java
index 81fcf624..44b4a83a 100644
--- a/src/main/java/io/github/randomcodespace/iq/cli/EnrichCommand.java
+++ b/src/main/java/io/github/randomcodespace/iq/cli/EnrichCommand.java
@@ -145,7 +145,7 @@ private int enrichFromCache(AnalysisCache cache, Path root, NumberFormat nf, Ins
CliOutput.step("\uD83C\uDFD7\uFE0F", "Detecting service boundaries...");
var serviceDetector = new io.github.randomcodespace.iq.analyzer.ServiceDetector();
String projectName = root.getFileName().toString();
- var serviceResult = serviceDetector.detect(enrichedNodes, enrichedEdges, projectName);
+ var serviceResult = serviceDetector.detect(enrichedNodes, enrichedEdges, projectName, root);
if (!serviceResult.serviceNodes().isEmpty()) {
// Add service nodes and edges to the builder
builder.addNodes(serviceResult.serviceNodes());
diff --git a/src/test/java/io/github/randomcodespace/iq/analyzer/ServiceDetectorTest.java b/src/test/java/io/github/randomcodespace/iq/analyzer/ServiceDetectorTest.java
index b4e25917..db939ffd 100644
--- a/src/test/java/io/github/randomcodespace/iq/analyzer/ServiceDetectorTest.java
+++ b/src/test/java/io/github/randomcodespace/iq/analyzer/ServiceDetectorTest.java
@@ -523,6 +523,38 @@ void deterministicWithContentExtraction(@TempDir Path tempDir) throws IOExceptio
}
}
+ @Test
+ void filesystemWalkFindsModulesNotPresentAsNodes(@TempDir Path tempDir) throws IOException {
+ // Simulate a .NET monorepo: .csproj files exist on disk but NO detector created CodeNodes for them
+ Files.createDirectories(tempDir.resolve("src/Basket.API"));
+ Files.writeString(tempDir.resolve("src/Basket.API/Basket.API.csproj"),
+ "", StandardCharsets.UTF_8);
+
+ Files.createDirectories(tempDir.resolve("src/Catalog.API"));
+ Files.writeString(tempDir.resolve("src/Catalog.API/Catalog.API.csproj"),
+ "", StandardCharsets.UTF_8);
+
+ Files.createDirectories(tempDir.resolve("src/Ordering.API"));
+ Files.writeString(tempDir.resolve("src/Ordering.API/Ordering.API.csproj"),
+ "", StandardCharsets.UTF_8);
+
+ // No nodes have build file paths — they are only on the filesystem
+ List nodes = new ArrayList<>();
+ nodes.add(makeNode("cls:BasketCtrl", NodeKind.CLASS, "BasketController",
+ "src/Basket.API/Controllers/BasketController.cs"));
+ nodes.add(makeNode("cls:CatalogCtrl", NodeKind.CLASS, "CatalogController",
+ "src/Catalog.API/Controllers/CatalogController.cs"));
+
+ var result = detector.detect(nodes, List.of(), "eShop", tempDir);
+
+ // Should detect 3 services via filesystem walk (not from node paths)
+ assertEquals(3, result.serviceNodes().size());
+ var names = result.serviceNodes().stream().map(CodeNode::getLabel).sorted().toList();
+ assertEquals(List.of("Basket.API", "Catalog.API", "Ordering.API"), names);
+ result.serviceNodes().forEach(svc ->
+ assertEquals("dotnet", svc.getProperties().get("build_tool")));
+ }
+
private static CodeNode makeNode(String id, NodeKind kind, String label, String filePath) {
CodeNode node = new CodeNode(id, kind, label);
node.setFilePath(filePath);