Skip to content

Commit 59f2e4e

Browse files
fix(intelligence): address PE architecture review — RepositoryIdentity constructor + AnalysisCache hash reuse
- GraphBuilder now accepts RepositoryIdentity + extractorVersion as constructor params; Provenance is derived internally (never constructed externally by callers) - Analyzer and EnrichCommand updated to pass RepositoryIdentity directly to GraphBuilder - AnalysisCache.getHashForPath() added for reverse path→hash lookup - buildFileInventory() now populates FileEntry.contentHash from cache (no file re-reads) Addresses BLOCKING 2 and BLOCKING 3 from PE review on RAN-150. Co-Authored-By: Paperclip <noreply@paperclip.ing>
1 parent b33baa9 commit 59f2e4e

4 files changed

Lines changed: 55 additions & 29 deletions

File tree

src/main/java/io/github/randomcodespace/iq/analyzer/Analyzer.java

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
import io.github.randomcodespace.iq.detector.DetectorResult;
1414
import io.github.randomcodespace.iq.detector.DetectorUtils;
1515
import io.github.randomcodespace.iq.grammar.AntlrParserFactory;
16-
import io.github.randomcodespace.iq.intelligence.CapabilityLevel;
1716
import io.github.randomcodespace.iq.intelligence.FileClassification;
1817
import io.github.randomcodespace.iq.intelligence.FileEntry;
1918
import io.github.randomcodespace.iq.intelligence.FileInventory;
20-
import io.github.randomcodespace.iq.intelligence.Provenance;
2119
import io.github.randomcodespace.iq.intelligence.RepositoryIdentity;
2220
import io.github.randomcodespace.iq.model.CodeEdge;
2321
import io.github.randomcodespace.iq.model.CodeNode;
@@ -236,14 +234,7 @@ private AnalysisResult runWithCache(Path root, Integer parallelism, AnalysisCach
236234

237235
// 1b. Resolve repository identity and build file inventory
238236
RepositoryIdentity repoIdentity = RepositoryIdentity.resolve(root);
239-
FileInventory fileInventory = buildFileInventory(files);
240-
Provenance provenance = new Provenance(
241-
repoIdentity.repoUrl(),
242-
repoIdentity.commitSha(),
243-
VersionCommand.VERSION,
244-
Provenance.CURRENT_SCHEMA_VERSION,
245-
CapabilityLevel.PARTIAL
246-
);
237+
FileInventory fileInventory = buildFileInventory(files, cache);
247238

248239
// Compute language breakdown
249240
Map<String, Integer> languageBreakdown = new HashMap<>();
@@ -318,7 +309,7 @@ private AnalysisResult runWithCache(Path root, Integer parallelism, AnalysisCach
318309

319310
// 3. Build graph (batched)
320311
report.accept("Building graph...");
321-
var builder = new GraphBuilder(provenance);
312+
var builder = new GraphBuilder(repoIdentity, VersionCommand.VERSION);
322313
int filesAnalyzed = 0;
323314
for (int i = 0; i < resultSlots.length; i++) {
324315
DetectorResult result = resultSlots[i];
@@ -347,7 +338,7 @@ private AnalysisResult runWithCache(Path root, Integer parallelism, AnalysisCach
347338
String projectDirName = root.getFileName() != null ? root.getFileName().toString() : "root";
348339
var serviceResult = serviceDetector.detect(allNodes, builder.getEdges(), projectDirName, root);
349340
if (!serviceResult.serviceNodes().isEmpty()) {
350-
serviceResult.serviceNodes().forEach(n -> n.setProvenance(provenance));
341+
serviceResult.serviceNodes().forEach(n -> n.setProvenance(builder.getProvenance()));
351342
builder.addNodes(serviceResult.serviceNodes());
352343
builder.addEdges(serviceResult.serviceEdges());
353344
allNodes = builder.getNodes(); // refresh reference after adding service nodes
@@ -1278,14 +1269,16 @@ DetectorResult analyzeFile(DiscoveredFile file, Path repoPath, DetectorRegistry
12781269

12791270
/**
12801271
* Build a deterministic FileInventory from the list of discovered files.
1281-
* Content hashes are not computed here (too expensive at discovery time); they remain null.
1272+
* Content hashes are reused from {@code cache} when available (no re-read of files).
1273+
* Hashes remain null for files not yet present in the cache.
12821274
*/
1283-
private static FileInventory buildFileInventory(List<DiscoveredFile> files) {
1275+
private static FileInventory buildFileInventory(List<DiscoveredFile> files, AnalysisCache cache) {
12841276
List<FileEntry> entries = new ArrayList<>(files.size());
12851277
for (DiscoveredFile f : files) {
12861278
String relPath = f.path().toString().replace('\\', '/');
12871279
FileClassification cls = FileEntry.classify(relPath, f.language());
1288-
entries.add(new FileEntry(relPath, f.language(), f.sizeBytes(), null, cls));
1280+
String contentHash = cache != null ? cache.getHashForPath(relPath) : null;
1281+
entries.add(new FileEntry(relPath, f.language(), f.sizeBytes(), contentHash, cls));
12891282
}
12901283
return new FileInventory(entries);
12911284
}

src/main/java/io/github/randomcodespace/iq/analyzer/GraphBuilder.java

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
import io.github.randomcodespace.iq.analyzer.linker.LinkResult;
44
import io.github.randomcodespace.iq.analyzer.linker.Linker;
55
import io.github.randomcodespace.iq.detector.DetectorResult;
6+
import io.github.randomcodespace.iq.intelligence.CapabilityLevel;
67
import io.github.randomcodespace.iq.intelligence.Provenance;
8+
import io.github.randomcodespace.iq.intelligence.RepositoryIdentity;
79
import io.github.randomcodespace.iq.model.CodeEdge;
810
import io.github.randomcodespace.iq.model.CodeNode;
911
import org.slf4j.Logger;
@@ -45,15 +47,38 @@ public GraphBuilder(int batchSize) {
4547
this(batchSize, null);
4648
}
4749

48-
public GraphBuilder(Provenance provenance) {
49-
this(1000, provenance);
50+
/**
51+
* Construct with repository identity and extractor version.
52+
* Provenance is derived internally from the identity.
53+
*/
54+
public GraphBuilder(RepositoryIdentity identity, String extractorVersion) {
55+
this(1000, identity, extractorVersion);
56+
}
57+
58+
/**
59+
* Construct with batch size, repository identity, and extractor version.
60+
* Provenance is derived internally from the identity.
61+
*/
62+
public GraphBuilder(int batchSize, RepositoryIdentity identity, String extractorVersion) {
63+
this(batchSize, identity == null ? null : new Provenance(
64+
identity.repoUrl(),
65+
identity.commitSha(),
66+
extractorVersion,
67+
Provenance.CURRENT_SCHEMA_VERSION,
68+
CapabilityLevel.PARTIAL
69+
));
5070
}
5171

52-
public GraphBuilder(int batchSize, Provenance provenance) {
72+
private GraphBuilder(int batchSize, Provenance provenance) {
5373
this.batchSize = Math.max(1, batchSize);
5474
this.provenance = provenance;
5575
}
5676

77+
/** Returns the provenance stamped on every node, or null if none was configured. */
78+
public Provenance getProvenance() {
79+
return provenance;
80+
}
81+
5782
/**
5883
* Add all nodes and edges from a detector result.
5984
*/

src/main/java/io/github/randomcodespace/iq/cache/AnalysisCache.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,23 @@ public synchronized boolean isCached(String contentHash) {
153153
}
154154
}
155155

156+
/**
157+
* Look up the content hash stored for a given file path.
158+
* Returns null if the path has not been cached yet.
159+
*/
160+
public synchronized String getHashForPath(String filePath) {
161+
try (var stmt = conn.prepareStatement(
162+
"SELECT content_hash FROM files WHERE path = ? LIMIT 1")) {
163+
stmt.setString(1, filePath);
164+
try (ResultSet rs = stmt.executeQuery()) {
165+
return rs.next() ? rs.getString(1) : null;
166+
}
167+
} catch (SQLException e) {
168+
log.debug("Hash lookup by path failed", e);
169+
return null;
170+
}
171+
}
172+
156173
// --- Store results ---
157174

158175
/**

src/main/java/io/github/randomcodespace/iq/cli/EnrichCommand.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
import io.github.randomcodespace.iq.analyzer.linker.Linker;
66
import io.github.randomcodespace.iq.cache.AnalysisCache;
77
import io.github.randomcodespace.iq.config.CodeIqConfig;
8-
import io.github.randomcodespace.iq.intelligence.CapabilityLevel;
9-
import io.github.randomcodespace.iq.intelligence.Provenance;
108
import io.github.randomcodespace.iq.intelligence.RepositoryIdentity;
119
import io.github.randomcodespace.iq.model.CodeEdge;
1210
import io.github.randomcodespace.iq.model.CodeNode;
@@ -120,14 +118,7 @@ private int enrichFromCache(AnalysisCache cache, Path root, NumberFormat nf, Ins
120118
// 2. Run linkers (these work on in-memory node/edge lists)
121119
CliOutput.step("\uD83D\uDD17", "Running cross-file linkers...");
122120
RepositoryIdentity repoIdentity = RepositoryIdentity.resolve(root);
123-
Provenance provenance = new Provenance(
124-
repoIdentity.repoUrl(),
125-
repoIdentity.commitSha(),
126-
VersionCommand.VERSION,
127-
Provenance.CURRENT_SCHEMA_VERSION,
128-
CapabilityLevel.PARTIAL
129-
);
130-
var builder = new GraphBuilder(provenance);
121+
var builder = new GraphBuilder(repoIdentity, VersionCommand.VERSION);
131122
for (CodeNode node : allNodes) {
132123
builder.addNodes(List.of(node));
133124
}
@@ -158,7 +149,7 @@ private int enrichFromCache(AnalysisCache cache, Path root, NumberFormat nf, Ins
158149
String projectName = root.getFileName().toString();
159150
var serviceResult = serviceDetector.detect(enrichedNodes, enrichedEdges, projectName, root);
160151
if (!serviceResult.serviceNodes().isEmpty()) {
161-
serviceResult.serviceNodes().forEach(n -> n.setProvenance(provenance));
152+
serviceResult.serviceNodes().forEach(n -> n.setProvenance(builder.getProvenance()));
162153
// Add service nodes and edges to the builder
163154
builder.addNodes(serviceResult.serviceNodes());
164155
builder.addEdges(serviceResult.serviceEdges());

0 commit comments

Comments
 (0)