Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
6c80884
fix: Allow async dispatches and error page rendering without authenti…
rostilos Jan 23, 2026
b5cab46
fix: Add analysis lock checks to prevent duplicate PR analysis in web…
rostilos Jan 23, 2026
3bc5967
fix: Implement atomic upsert for command rate limiting to prevent rac…
rostilos Jan 23, 2026
28a4007
fix: Improve alias management by ensuring direct collections are dele…
rostilos Jan 23, 2026
efc42d2
fix: Enhance locking mechanism in PR webhook handlers to prevent race…
rostilos Jan 23, 2026
13a63c8
fix: Enhance alias management by implementing backup and migration st…
rostilos Jan 23, 2026
95d74e1
fix: Enhance AI analysis by incorporating full PR issue history and r…
rostilos Jan 23, 2026
e509ffc
fix: Update issue reconciliation logic to handle previous issues in b…
rostilos Jan 23, 2026
a2889c7
fix: Improve handling of issue resolution status and logging for bett…
rostilos Jan 23, 2026
6fb5693
fix: Implement method to retrieve branch differences from GitLab API
rostilos Jan 23, 2026
56761fb
fix: Enhance logging and implement deterministic context retrieval in…
rostilos Jan 23, 2026
8fc46f3
Refactor AI connection handling and improve job deletion logic
rostilos Jan 26, 2026
7c78057
feat: Add pre-acquired lock key to prevent double-locking in PR analy…
rostilos Jan 26, 2026
6d80d71
feat: Implement handling for AnalysisLockedException and DiffTooLarge…
rostilos Jan 26, 2026
e2c1474
feat: Re-fetch job entities in transaction methods to handle detached…
rostilos Jan 27, 2026
342c4fa
feat: Update JobService and WebhookAsyncProcessor to manage job entit…
rostilos Jan 27, 2026
409c42d
feat: Enable transaction management in processWebhookAsync to support…
rostilos Jan 27, 2026
11c983c
feat: Re-fetch job entities in JobService methods to ensure consisten…
rostilos Jan 27, 2026
c75eaba
feat: Add @Transactional annotation to processWebhookAsync for lazy l…
rostilos Jan 27, 2026
8afc0ad
feat: Implement self-injection in WebhookAsyncProcessor for proper tr…
rostilos Jan 27, 2026
402486b
feat: Enhance logging and error handling in processWebhookAsync for i…
rostilos Jan 27, 2026
fdcdca0
feat: Implement webhook deduplication service to prevent duplicate co…
rostilos Jan 27, 2026
e321361
feat: Enhance job deletion process with logging and persistence conte…
rostilos Jan 27, 2026
ebd0fad
feat: Improve job deletion process with enhanced logging and error ha…
rostilos Jan 27, 2026
092b361
feat: Add method to delete job by ID in JobRepository and update JobS…
rostilos Jan 27, 2026
61d2620
feat: Simplify job handling by marking ignored jobs as SKIPPED instea…
rostilos Jan 27, 2026
704a7a2
feat: Enhance AI connection logging and refactor placeholder manageme…
rostilos Jan 28, 2026
2e42ebc
feat: Add logging for LLM creation and enhance diff snippet extractio…
rostilos Jan 28, 2026
d036fa9
feat: Implement AST-based code splitter and scoring configuration
rostilos Jan 28, 2026
642bda0
feat: Enhance lock management in PullRequestAnalysisProcessor and imp…
rostilos Jan 28, 2026
5add89c
feat: Enhance AST processing and metadata extraction in RAG pipeline …
rostilos Jan 28, 2026
1fc484c
feat: Improve deduplication strategy in RAGQueryService to prioritize…
rostilos Jan 28, 2026
c03591e
feat: Enhance comments for clarity on target branch indexing and incr…
rostilos Jan 28, 2026
0bb9ca8
feat: Update default configuration values for chunk size and text chu…
rostilos Jan 28, 2026
3bb9025
feat: Implement PR-specific indexing and hybrid query support in RAG …
rostilos Jan 29, 2026
29859ee
feat: Enhance PR indexing with improved error handling and content pr…
rostilos Jan 29, 2026
7770367
feat: Add resolution tracking fields to CodeReviewIssue and enhance i…
rostilos Jan 30, 2026
c9899fc
Refactor RAG pipeline: Remove deprecated methods and classes, update …
rostilos Feb 1, 2026
2ae1345
Batch review in MCP-client, batch size from 3 to 5 files from a diff
rostilos Feb 1, 2026
6ad8ed9
feat: Enhance webhook processing and deduplication
rostilos Feb 1, 2026
51c876d
Refactor security annotations in controllers to use custom annotations
rostilos Feb 2, 2026
fc67021
Refactor embedding model handling and add AST parsing endpoints
rostilos Feb 3, 2026
c90cb4e
feat: Implement optimistic locking in VcsConnection and enhance trans…
rostilos Feb 4, 2026
2958327
feat: Add workspace management interface and implement service method…
rostilos Feb 5, 2026
5dfa058
feat: Enhance VcsConnection with optimistic locking and set default v…
rostilos Feb 8, 2026
07754fa
Merge pull request #114 from rostilos/epic/CA-7-pr-review-process-flow
rostilos Feb 8, 2026
bb8918a
feat: Enhance ReviewService with concurrency control and timeout hand…
rostilos Feb 8, 2026
e87fced
Merge pull request #116 from rostilos/epic/CA-7-pr-review-process-flow
rostilos Feb 8, 2026
b89fd99
feat: Implement diff fingerprint caching for pull request analysis
rostilos Feb 9, 2026
c509092
feat: Add service secret configuration for RAG API and update tests f…
rostilos Feb 9, 2026
36a8cea
Merge pull request #118 from rostilos/feature/CA-10-commit-based-anal…
rostilos Feb 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ llm.sync.anthropic.api-key=
#RAG
codecrow.rag.api.url=http://host.docker.internal:8001
codecrow.rag.api.enabled=true
codecrow.rag.api.secret=change-me-to-a-random-secret
# RAG API timeouts (in seconds)
codecrow.rag.api.timeout.connect=30
codecrow.rag.api.timeout.read=120
Expand Down
11 changes: 11 additions & 0 deletions deployment/config/mcp-client/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ AI_CLIENT_PORT=8000
RAG_ENABLED=true
RAG_API_URL=http://host.docker.internal:8001

# === Service-to-Service Auth ===
# Shared secret for authenticating requests between internal services.
# Must match the SERVICE_SECRET configured on rag-pipeline.
# Leave empty to disable auth (dev mode only).
# IMPORTANT: Avoid $ { } characters in the secret — they can cause dotenv parsing issues.
SERVICE_SECRET=change-me-to-a-random-secret

# === Concurrency ===
# Max parallel review requests handled simultaneously (default: 4)
MAX_CONCURRENT_REVIEWS=4

# API access for Platform MCP (internal network only)
CODECROW_API_URL=http://codecrow-web-application:8081

Expand Down
30 changes: 30 additions & 0 deletions deployment/config/rag-pipeline/.env.sample
Original file line number Diff line number Diff line change
@@ -1,7 +1,37 @@
# === Service-to-Service Auth ===
# Shared secret for authenticating incoming requests from mcp-client.
# Must match the SERVICE_SECRET configured on mcp-client.
# Leave empty to disable auth (dev mode only).
# IMPORTANT: Avoid $ { } characters in the secret — they can cause dotenv parsing issues.
SERVICE_SECRET=change-me-to-a-random-secret

# === Path Traversal Guard ===
# Root directory that repo_path arguments are allowed under.
# The rag-pipeline will reject any index/query request whose resolved
# path escapes this directory. Default: /tmp
ALLOWED_REPO_ROOT=/tmp

#QDRANT configuration
QDRANT_URL=http://qdrant:6333
QDRANT_COLLECTION_PREFIX=codecrow

# ollama/openrouter
EMBEDDING_PROVIDER=openrouter
OLLAMA_BASE_URL=http://localhost:11434
OLLAMA_EMBEDDING_MODEL=qwen3-embedding:0.6b

# Ollama Performance Tuning
# Batch size for embedding requests (higher = better throughput, more memory)
OLLAMA_BATCH_SIZE=100
# Request timeout in seconds (increase for slow CPU)
OLLAMA_TIMEOUT=120

# CPU Threading Optimization (set based on your CPU cores)
# Recommended: physical_cores - 1 (leave 1 core for system)
OMP_NUM_THREADS=6
MKL_NUM_THREADS=6
OPENBLAS_NUM_THREADS=6

# OpenRouter Configuration
# Get your API key from https://openrouter.ai/
OPENROUTER_API_KEY=sk-or-v1-your-api-key-here
Expand Down
20 changes: 20 additions & 0 deletions deployment/config/web-frontend/.env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ VITE_WEBHOOK_URL=http://localhost:8082
VITE_GOOGLE_CLIENT_ID=YOUR_GOOGLE_CLIENT_ID
SERVER_PORT=8080

VITE_BLOG_URL=http://localhost:8083

# ============================================================================
# Feature Flags (Cloud-specific features - disabled by default for OSS)
# ============================================================================
# Set to "true" to enable cloud features. Leave empty or "false" to disable.
#
# VITE_FEATURE_BILLING - Enables billing page, subscription management, payment methods
VITE_FEATURE_BILLING=false
#
# VITE_FEATURE_CLOUD_PLANS - Enables cloud subscription plans (Pro, Pro+, Enterprise)
VITE_FEATURE_CLOUD_PLANS=false
#
# VITE_FEATURE_USAGE_ANALYTICS - Enables usage tracking and quota management
VITE_FEATURE_USAGE_ANALYTICS=false
#
# VITE_FEATURE_ENTERPRISE - Enables SSO, SAML, and advanced team management
VITE_FEATURE_ENTERPRISE=false


# New Relic Browser Monitoring (Optional - leave empty to disable)
# Get these values from: https://one.newrelic.com -> Browser -> Add data -> Browser monitoring
# License Key: Use the BROWSER (Ingest) license key, NOT the main license key
Expand Down
6 changes: 6 additions & 0 deletions java-ecosystem/libs/analysis-engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
<artifactId>okhttp</artifactId>
</dependency>

<!-- JTokkit for token counting -->
<dependency>
<groupId>com.knuddels</groupId>
<artifactId>jtokkit</artifactId>
</dependency>

<!-- Test Dependencies -->
<dependency>
<groupId>org.junit.jupiter</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
requires com.fasterxml.jackson.annotation;
requires jakarta.persistence;
requires kotlin.stdlib;
requires jtokkit;

exports org.rostilos.codecrow.analysisengine.aiclient;
exports org.rostilos.codecrow.analysisengine.config;
exports org.rostilos.codecrow.analysisengine.dto.request.ai;
exports org.rostilos.codecrow.analysisengine.dto.request.ai.enrichment;
exports org.rostilos.codecrow.analysisengine.dto.request.processor;
exports org.rostilos.codecrow.analysisengine.dto.request.validation;
exports org.rostilos.codecrow.analysisengine.exception;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public Map<String, Object> performAnalysis(AiAnalysisRequest request, java.util.

/**
* Extracts analysis data from nested response structure.
* Expected: response -> result -> {comment, issues}
* Expected: response -> result -> {comment, issues, inference_stats}
* Issues can be either a List (array) or Map (object with numeric keys)
*/
private Map<String, Object> extractAndValidateAnalysisData(Map response) throws IOException {
Expand All @@ -176,6 +176,15 @@ private Map<String, Object> extractAndValidateAnalysisData(Map response) throws
if (result == null) {
throw new IOException("Missing 'result' field in AI response");
}

// Check for error response from MCP client
Object errorFlag = result.get("error");
if (Boolean.TRUE.equals(errorFlag) || "true".equals(String.valueOf(errorFlag))) {
String errorMessage = result.get("error_message") != null
? String.valueOf(result.get("error_message"))
: String.valueOf(result.get("comment"));
throw new IOException("Analysis failed: " + errorMessage);
}

if (!result.containsKey("comment") || !result.containsKey("issues")) {
throw new IOException("Analysis data missing required fields: 'comment' and/or 'issues'");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.rostilos.codecrow.analysisengine.dto.request.ai;

Check failure on line 1 in java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/dto/request/ai/AiAnalysisRequestImpl.java

View check run for this annotation

CodeCrow-Local / CodeCrow Analysis

HIGH severity issue

The deduplication logic in 'withAllPrAnalysesData' fails to merge resolved status when the input list 'allPrAnalyses' is sorted DESC (newest first), as specified in the Javadoc. In DESC order, the first issue seen for a fingerprint is the newest. Subsequent (older) issues will have a lower 'currentVersion' than the 'existingVersion' in the map, causing the 'if (currentVersion > existingVersion)' block to be skipped. Consequently, if an older version was resolved but the newest is open, the re...
Raw output
Suggested fix:
Sort the input list in ascending order (oldest first) before processing, or adjust the logic to handle DESC order by checking if the older (current) issue is resolved when the existing (newer) one is not.

import com.fasterxml.jackson.annotation.JsonProperty;
import org.rostilos.codecrow.analysisengine.dto.request.ai.enrichment.PrEnrichmentDataDto;
import org.rostilos.codecrow.core.model.ai.AIConnection;
import org.rostilos.codecrow.core.model.ai.AIProviderKey;
import org.rostilos.codecrow.core.model.codeanalysis.AnalysisMode;
Expand Down Expand Up @@ -44,6 +45,9 @@
protected final String deltaDiff;
protected final String previousCommitHash;
protected final String currentCommitHash;

// File enrichment data (full file contents + dependency graph)
protected final PrEnrichmentDataDto enrichmentData;

protected AiAnalysisRequestImpl(Builder<?> builder) {
this.projectId = builder.projectId;
Expand Down Expand Up @@ -74,6 +78,8 @@
this.deltaDiff = builder.deltaDiff;
this.previousCommitHash = builder.previousCommitHash;
this.currentCommitHash = builder.currentCommitHash;
// File enrichment data
this.enrichmentData = builder.enrichmentData;
}

public Long getProjectId() {
Expand Down Expand Up @@ -181,6 +187,10 @@
return currentCommitHash;
}

public PrEnrichmentDataDto getEnrichmentData() {
return enrichmentData;
}


public static Builder<?> builder() {
return new Builder<>();
Expand Down Expand Up @@ -216,6 +226,8 @@
private String deltaDiff;
private String previousCommitHash;
private String currentCommitHash;
// File enrichment data
private PrEnrichmentDataDto enrichmentData;

protected Builder() {
}
Expand Down Expand Up @@ -278,6 +290,113 @@
return self();
}

/**
* Set previous issues from ALL PR analysis versions.
* This provides the LLM with complete issue history including resolved issues,
* helping it understand what was already found and fixed.
*
* Issues are deduplicated by fingerprint (file + line ±3 + severity + truncated reason).
* When duplicates exist across versions, we keep the most recent version's data
* but preserve resolved status if ANY version marked it resolved.
*
* @param allPrAnalyses List of all analyses for this PR, ordered by version DESC (newest first)
*/
public T withAllPrAnalysesData(List<CodeAnalysis> allPrAnalyses) {
if (allPrAnalyses == null || allPrAnalyses.isEmpty()) {
return self();
}

// Convert all issues to DTOs
List<AiRequestPreviousIssueDTO> allIssues = allPrAnalyses.stream()
.flatMap(analysis -> analysis.getIssues().stream())
.map(AiRequestPreviousIssueDTO::fromEntity)
.toList();

// Deduplicate: group by fingerprint, keep most recent version but preserve resolved status
java.util.Map<String, AiRequestPreviousIssueDTO> deduped = new java.util.LinkedHashMap<>();

for (AiRequestPreviousIssueDTO issue : allIssues) {
String fingerprint = computeIssueFingerprint(issue);
AiRequestPreviousIssueDTO existing = deduped.get(fingerprint);

if (existing == null) {
// First occurrence of this issue
deduped.put(fingerprint, issue);
} else {
// Duplicate found - keep the one with higher prVersion (more recent)
// But if older version is resolved and newer is not, preserve resolved status
int existingVersion = existing.prVersion() != null ? existing.prVersion() : 0;
int currentVersion = issue.prVersion() != null ? issue.prVersion() : 0;

boolean existingResolved = "resolved".equalsIgnoreCase(existing.status());
boolean currentResolved = "resolved".equalsIgnoreCase(issue.status());

if (currentVersion > existingVersion) {
// Current is newer - use it, but preserve resolved status if existing was resolved
if (existingResolved && !currentResolved) {
// Older version was resolved but newer one isn't marked - use resolved data from older
deduped.put(fingerprint, mergeResolvedStatus(issue, existing));
} else {
deduped.put(fingerprint, issue);
}
} else if (existingVersion == currentVersion) {
// Same version - prefer resolved one
if (currentResolved && !existingResolved) {
deduped.put(fingerprint, issue);
}
}
// If existing is newer, keep it (already in map)
}
}

this.previousCodeAnalysisIssues = new java.util.ArrayList<>(deduped.values());

return self();
}

/**
* Compute a fingerprint for an issue to detect duplicates across PR versions.
* Uses: file + normalized line (±3 tolerance) + severity + first 50 chars of reason.
*/
private String computeIssueFingerprint(AiRequestPreviousIssueDTO issue) {
String file = issue.file() != null ? issue.file() : "";
// Normalize line to nearest multiple of 3 for tolerance
int lineGroup = issue.line() != null ? (issue.line() / 3) : 0;

Check warning on line 364 in java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/dto/request/ai/AiAnalysisRequestImpl.java

View check run for this annotation

CodeCrow-Local / CodeCrow Analysis

MEDIUM severity issue

The line grouping logic 'issue.line() / 3' does not implement the '±3 tolerance' mentioned in the Javadoc. This creates fixed buckets (e.g., lines 1-2 in bucket 0, lines 3-5 in bucket 1). Adjacent lines like 2 and 3 will fall into different buckets and fail to match, while lines 3 and 5 will match despite being further apart. This makes issue tracking across commits jittery.
Raw output
Suggested fix:
Consider using a larger bucket or a more flexible matching algorithm if true tolerance is required. At minimum, update the Javadoc to reflect that it uses fixed buckets of size 3.
String severity = issue.severity() != null ? issue.severity() : "";
String reasonPrefix = issue.reason() != null
? issue.reason().substring(0, Math.min(50, issue.reason().length())).toLowerCase().trim()
: "";

return file + "::" + lineGroup + "::" + severity + "::" + reasonPrefix;
}

/**
* Merge resolved status from an older issue version into a newer one.
* Creates a new DTO with the newer issue's data but the older issue's resolution info.
*/
private AiRequestPreviousIssueDTO mergeResolvedStatus(
AiRequestPreviousIssueDTO newer,
AiRequestPreviousIssueDTO resolvedOlder) {
return new AiRequestPreviousIssueDTO(
newer.id(),
newer.type(),
newer.severity(),
newer.reason(),
newer.suggestedFixDescription(),
newer.suggestedFixDiff(),
newer.file(),
newer.line(),
newer.branch(),
newer.pullRequestId(),
resolvedOlder.status(), // Use resolved status from older
newer.category(),
newer.prVersion(),
resolvedOlder.resolvedDescription(),
resolvedOlder.resolvedByCommit(),
resolvedOlder.resolvedInAnalysisId()
);
}

public T withMaxAllowedTokens(int maxAllowedTokens) {
this.maxAllowedTokens = maxAllowedTokens;
return self();
Expand Down Expand Up @@ -354,6 +473,11 @@
return self();
}

public T withEnrichmentData(PrEnrichmentDataDto enrichmentData) {
this.enrichmentData = enrichmentData;
return self();
}

public AiAnalysisRequestImpl build() {
return new AiAnalysisRequestImpl(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,23 @@ public record AiRequestPreviousIssueDTO(
String branch,
String pullRequestId,
String status, // open|resolved|ignored
String category
String category,
// Resolution tracking fields
Integer prVersion, // Which PR iteration this issue was found in
String resolvedDescription, // Description of how the issue was resolved
String resolvedByCommit, // Commit hash that resolved the issue
Long resolvedInAnalysisId // Analysis ID where this was resolved (null if still open)
) {
public static AiRequestPreviousIssueDTO fromEntity(CodeAnalysisIssue issue) {
String categoryStr = issue.getIssueCategory() != null
? issue.getIssueCategory().name()
: IssueCategory.CODE_QUALITY.name();

Integer prVersion = null;
if (issue.getAnalysis() != null) {
prVersion = issue.getAnalysis().getPrVersion();
}

return new AiRequestPreviousIssueDTO(
String.valueOf(issue.getId()),
categoryStr,
Expand All @@ -33,7 +44,11 @@ public static AiRequestPreviousIssueDTO fromEntity(CodeAnalysisIssue issue) {
issue.getAnalysis() == null ? null : issue.getAnalysis().getBranchName(),
issue.getAnalysis() == null || issue.getAnalysis().getPrNumber() == null ? null : String.valueOf(issue.getAnalysis().getPrNumber()),
issue.isResolved() ? "resolved" : "open",
categoryStr
categoryStr,
prVersion,
issue.getResolvedDescription(),
issue.getResolvedCommitHash(),
issue.getResolvedAnalysisId()
);
}
}
Loading