-
Notifications
You must be signed in to change notification settings - Fork 0
0.2.0 rc #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…pic + openai + google providers compatibility for MCP client. MCP-client error handling
…ling ( pipeline-agent side )
…upport for new Gemini-3 preview models
…nhance issue count updates
|
Caution Review failedThe pull request is closed. 📝 WalkthroughWalkthroughAdds PR-aware and commit-range diffs, incremental analysis metadata, GitLab integration, command-authorization with allowed-user sync, multi-provider LLM support (including Google), error sanitization, semantic/AST splitters, many web-server package reorganizations, MCP artifact rename, DB migrations, and related DTO/service changes. Changes
Sequence Diagram(s)sequenceDiagram
participant Webhook as VCS Webhook
participant Parser as Webhook Parser
participant Controller as ProviderWebhookController
participant Handler as Branch/PR Handler
participant VcsOps as VcsOperationsService
participant Analyzer as Analysis Engine
participant Store as BranchRepository
Webhook->>Parser: deliver event (push / pullrequest /merge /comment)
Parser->>Controller: WebhookPayload (includes prAuthorId/prAuthorUsername, sourcePrNumber)
Controller->>Handler: route to PR handler / branch handler / comment handler
alt Branch analysis (merge or push)
Handler->>VcsOps: if sourcePrNumber != null -> getPullRequestDiff(...) else getCommitDiff(...)
VcsOps-->>Handler: unified diff
Handler->>Analyzer: build AiAnalysisRequest (analysisMode, deltaDiff?, commits) and analyze
Analyzer-->>Handler: analysis result (issues)
Handler->>Store: reload branch, update issue counts, persist
Store-->>Handler: persisted
else PR analysis
Handler->>VcsOps: getPullRequestDiff(...) and optionally getCommitRangeDiff(base, head)
VcsOps-->>Handler: full diff and/or delta diff
Handler->>Analyzer: choose INCREMENTAL or FULL, analyze(diff)
Analyzer-->>Handler: analysis result (issues)
Handler->>Store: persist PR/analysis and branch issues
else Command comment
Handler->>AuthSvc: checkAuthorization(project, payload, prAuthorId, prAuthorUsername)
AuthSvc-->>Handler: allowed/denied
alt allowed
Handler->>Processor: execute command
else denied
Handler-->>Controller: deny response / skip
end
end
sequenceDiagram
actor Client as User/Integration
participant Web as Web Server (API)
participant VcsSvc as VcsConnectionWebService
participant Repo as VcsClientProvider / VcsClient
participant SyncSvc as AllowedCommandUserService
participant RepoDB as AllowedCommandUserRepository
Client->>Web: request "sync allowed users" for project
Web->>VcsSvc: resolve VCS connection info
VcsSvc->>Repo: create authorized VcsClient and fetch collaborators
Repo-->>VcsSvc: list of collaborators
VcsSvc->>SyncSvc: pass collaborators for upsert
SyncSvc->>RepoDB: upsert AllowedCommandUser rows (add/update)
RepoDB-->>SyncSvc: ack counts
SyncSvc-->>Web: sync result (added/updated/total)
Web-->>Client: HTTP response with SyncResult
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
📜 Recent review detailsConfiguration used: defaults Review profile: CHILL Plan: Pro 📒 Files selected for processing (7)
Comment |
|
/codecrow review |
|
/codecrow review |
|
/codecrow review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 7
🧹 Nitpick comments (5)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookAsyncProcessor.java (1)
455-464: Consider adding explicit sensitive keyword filtering.The fallback logic at line 463 returns the raw message if it doesn't match the length/pattern filters. Messages containing sensitive terms like "password", "secret", "credential", or "token" (outside the token-limit context) could potentially slip through.
🔎 Suggested enhancement
// For other errors, provide a generic message but don't expose technical details // Only show the first 200 chars if they don't contain sensitive info if (errorMessage.length() > 200 || errorMessage.contains("{") || - errorMessage.contains("Exception") || errorMessage.contains("at org.")) { + errorMessage.contains("Exception") || errorMessage.contains("at org.") || + lowerMessage.contains("password") || lowerMessage.contains("secret") || + lowerMessage.contains("credential") || lowerMessage.contains("key=")) { return "An error occurred while processing your request. " + "Please check the job logs for more details."; }python-ecosystem/mcp-client/utils/error_sanitizer.py (1)
63-64: Silent exception suppression hides potential issues.The bare
except Exception: passsilently swallows all errors during regex extraction. At minimum, log at debug level for troubleshooting.🔎 Proposed fix
except Exception: - pass + logger.debug("Failed to extract alternative model suggestion from error message")python-ecosystem/mcp-client/service/review_service.py (1)
210-222: Error sanitization correctly integrated; minor linting fixes needed.The error sanitization pattern is well-implemented. Note:
create_user_friendly_erroralready logs the error internally, so thelogger.errorcall at line 212 results in double logging.🔎 Fix linting issues and remove duplicate logging
except Exception as e: - # Log full error for debugging, but sanitize for user display - logger.error(f"Review processing failed: {str(e)}", exc_info=True) + # create_user_friendly_error logs internally; exc_info for stack trace + logger.debug("Review processing failed", exc_info=True) sanitized_message = create_user_friendly_error(e) error_response = ResponseParser.create_error_response( - f"Agent execution failed", sanitized_message + "Agent execution failed", sanitized_message )python-ecosystem/mcp-client/service/command_service.py (1)
17-17: Unused import:sanitize_error_for_display.Only
create_user_friendly_erroris used in this file. Consider removing the unused import.🔎 Remove unused import
-from utils.error_sanitizer import sanitize_error_for_display, create_user_friendly_error +from utils.error_sanitizer import create_user_friendly_errorpython-ecosystem/mcp-client/llm/llm_factory.py (1)
188-190: Gemini 3 detection could miss model variations.The check
"gemini-3" in model_lower or "gemini3" in model_lowermay not catch all Gemini 3 model naming conventions (e.g.,gemini-3.0-pro,gemini-3-flash).🔎 More robust Gemini 3 detection
model_lower = ai_model.lower() - is_gemini_3 = "gemini-3" in model_lower or "gemini3" in model_lower + # Match gemini-3.x or gemini3 patterns + import re + is_gemini_3 = bool(re.search(r'gemini[-_]?3', model_lower))
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (22)
java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/dto/request/processor/BranchProcessRequest.javajava-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/processor/analysis/BranchAnalysisProcessor.javajava-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/service/vcs/VcsOperationsService.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/ai/AIProviderKey.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/job/Job.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/job/JobStatus.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/JobService.javajava-ecosystem/libs/core/src/main/resources/db/migration/V23__add_google_provider_key.sqljava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/handler/BitbucketCloudBranchWebhookHandler.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/service/BitbucketOperationsService.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderPipelineActionController.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookAsyncProcessor.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/github/service/GitHubOperationsService.javapython-ecosystem/.gitignorepython-ecosystem/mcp-client/.gitignorepython-ecosystem/mcp-client/llm/llm_factory.pypython-ecosystem/mcp-client/requirements.txtpython-ecosystem/mcp-client/service/command_service.pypython-ecosystem/mcp-client/service/review_service.pypython-ecosystem/mcp-client/utils/context_builder.pypython-ecosystem/mcp-client/utils/error_sanitizer.pypython-ecosystem/rag-pipeline/.gitignore
💤 Files with no reviewable changes (1)
- python-ecosystem/.gitignore
🧰 Additional context used
🧬 Code graph analysis (2)
python-ecosystem/mcp-client/service/review_service.py (3)
python-ecosystem/mcp-client/utils/error_sanitizer.py (2)
sanitize_error_for_display(12-141)create_user_friendly_error(144-161)python-ecosystem/mcp-client/utils/response_parser.py (1)
create_error_response(676-702)python-ecosystem/mcp-client/service/command_service.py (1)
_emit_event(935-941)
python-ecosystem/mcp-client/service/command_service.py (1)
python-ecosystem/mcp-client/utils/error_sanitizer.py (2)
sanitize_error_for_display(12-141)create_user_friendly_error(144-161)
🪛 GitHub Check: CodeCrow Analysis
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookAsyncProcessor.java
[warning] 417-417: MEDIUM severity issue
sanitizeErrorForVcs uses broad lowerMessage.contains("model") && ("not found"|"invalid"|...) which misclassifies non-AI errors (e.g., file/model config issues) as model problems, giving wrong remediation advice.
python-ecosystem/mcp-client/utils/error_sanitizer.py
[warning] 47-47: MEDIUM severity issue
Broad check 'model' in error_lower && any('not found'|'invalid'|...) falsely classifies unrelated errors (e.g., 'config model file not found', 'database model invalid') as AI model issues, providing misleading user guidance.
[notice] 150-150: LOW severity issue
Regex only redacts OpenAI 'sk-[a-zA-Z0-9]{20,}' and generic 'api_key=xxx'. Misses Anthropic 'sk-ant-[a-zA-Z0-9]{59}', Google 'AIzaSy[a-zA-Z0-9-_]{35}', OpenRouter bearer tokens, risking exposure in sanitized errors shown to users.
python-ecosystem/mcp-client/llm/llm_factory.py
[warning] 50-50: MEDIUM severity issue
New multi-provider logic (normalization, unsupported checks, Anthropic/Google instantiation, error raising) lacks unit tests. Risk of regressions in provider selection, model validation, kwarg handling without coverage.
[notice] 190-190: LOW severity issue
Gemini 3 models override temperature <=0 to 0.1 ("temperature=temperature if temperature > 0 else 0.1"), reducing determinism/consistency for code review/analysis where temperature=0.0 preferred.
🪛 Ruff (0.14.10)
python-ecosystem/mcp-client/service/review_service.py
212-212: Use explicit conversion flag
Replace with conversion flag
(RUF010)
216-216: f-string without any placeholders
Remove extraneous f prefix
(F541)
382-382: Use explicit conversion flag
Replace with conversion flag
(RUF010)
python-ecosystem/mcp-client/service/command_service.py
116-116: Use explicit conversion flag
Replace with conversion flag
(RUF010)
211-211: Use explicit conversion flag
Replace with conversion flag
(RUF010)
python-ecosystem/mcp-client/utils/error_sanitizer.py
63-64: try-except-pass detected, consider logging the exception
(S110)
63-63: Do not catch blind exception: Exception
(BLE001)
🔇 Additional comments (26)
python-ecosystem/mcp-client/.gitignore (1)
14-16: Ignore patterns for artifacts and logs look good.Adding
logs/**to prevent runtime log files from being tracked is a sensible practice, especially as PR processing generates transient logs. The*.jarpattern correctly preserves the Maven wrapper JAR exception on line 8.python-ecosystem/rag-pipeline/.gitignore (1)
21-22: Ignore pattern for logs is appropriate.Adding
logs/**to ignore transient log files aligns with the PR's PR-aware diff processing and prevents build/runtime artifacts from being tracked across the ecosystem.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookAsyncProcessor.java (2)
122-131: LGTM! Proper handling of ignored webhooks with SKIPPED status.Using
skipJobinstead of completing for ignored webhooks improves job lifecycle semantics—clearly distinguishing between successfully processed jobs and those that were intentionally not processed.
360-365: Good user-facing error handling.Sanitizing errors before posting to VCS and appending guidance to check logs is a solid pattern. It prevents leaking sensitive details while still directing users to where they can find more information.
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/job/JobStatus.java (1)
10-11: LGTM!The addition of
WAITINGandSKIPPEDstates expands the job lifecycle appropriately.SKIPPEDis correctly integrated withJob.skip()andJob.isTerminal()as shown in related changes.java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/ai/AIProviderKey.java (1)
6-7: LGTM!The
V23__add_google_provider_key.sql) that updates the constraint to include this new provider.java-ecosystem/libs/core/src/main/resources/db/migration/V23__add_google_provider_key.sql (1)
1-4: LGTM!The migration safely updates the constraint using
DROP CONSTRAINT IF EXISTSbefore re-adding with the expanded provider list. The constraint values align with theAIProviderKeyenum.java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/job/Job.java (2)
159-164: LGTM!The
skip()method follows the established pattern of other terminal state methods (fail(),cancel(),complete()). ReusingerrorMessagefor the skip reason is pragmatic and avoids schema changes, though semantically it's not an "error" message. This is an acceptable trade-off.
166-168: LGTM!The
isTerminal()method correctly includesSKIPPEDas a terminal state alongsideCOMPLETED,FAILED, andCANCELLED.java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/dto/request/processor/BranchProcessRequest.java (1)
20-26: LGTM!The
sourcePrNumberfield is well-documented and correctly enables PR-aware diff retrieval. The comment clearly explains when and why this field is used (forpullrequest:fulfilledevents to capture all PR files rather than just merge commit changes).java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/JobService.java (1)
297-307: LGTM!The
skipJob()method follows the established pattern ofcancelJob()andcompleteJob(), using standard@Transactionalpropagation. This is appropriate since skipping occurs in normal control flow (unlikefailJob()which usesREQUIRES_NEWfor error recovery scenarios). The implementation correctly persists, logs, and notifies subscribers.java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/processor/analysis/BranchAnalysisProcessor.java (2)
164-183: LGTM!The PR-aware diff retrieval logic is well-implemented. Using the PR diff (when
sourcePrNumberis available) ensures all files from the original PR are analyzed, which is important forpullrequest:fulfilledevents where the merge commit might not capture all changes.
197-208: LGTM!The early issue count update ensures branch statistics are accurate even when no pre-existing issues require reanalysis (when
candidateBranchIssuesis empty). This complements the existing update inreanalyzeCandidateIssues()which handles counts after AI reconciliation resolves issues. The inline comment clearly explains this rationale.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderPipelineActionController.java (2)
191-199: LGTM - Lock handling now correctly marks jobs as FAILED.The change from
cancelJobtofailJobwith an explicit lock timeout message is semantically appropriate. Lock timeouts represent a failure condition rather than a user-initiated cancellation, and this now provides clearer error messaging to users.
229-236: Consistent lock handling in the completion path.The same
failJobpattern is correctly applied in the async completion handler, maintaining consistency with the quick result path.python-ecosystem/mcp-client/service/review_service.py (1)
380-389: Same pattern applied correctly in streaming execution.Consistent error handling with sanitization. Same minor linting note about
f"{str(e)}"applies.python-ecosystem/mcp-client/service/command_service.py (4)
115-119: Error sanitization correctly applied to summarize workflow.The sanitization pattern is consistent with
review_service.py. Same note about potential double logging applies.
210-214: Error sanitization correctly applied to ask workflow.Consistent error handling across all command workflows.
679-682: Error handling in _execute_summarize is consistent.The sanitization is correctly applied to agent execution errors.
795-798: Error handling in _execute_ask is consistent.All error paths now return sanitized messages.
python-ecosystem/mcp-client/llm/llm_factory.py (2)
175-184: Anthropic provider integration looks correct.The
ChatAnthropicinstantiation follows LangChain patterns. Note thatparallel_tool_callsfrommodel_kwargsis not passed to Anthropic, which is appropriate since Anthropic handles this differently.
47-49: New exception class for unsupported providers.Clean addition that enables better error handling in the error sanitizer.
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/service/BitbucketOperationsService.java (1)
31-35: LGTM - PR diff retrieval follows established patterns.The implementation correctly delegates to
GetPullRequestDiffActionfollowing the same pattern asgetCommitDiff. Clean and consistent.java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/service/vcs/VcsOperationsService.java (1)
32-43: LGTM - Interface extended with well-documented PR diff method.The new
getPullRequestDiffmethod is properly documented, explaining that it returns ALL files changed in the PR, not just the merge commit. This distinction fromgetCommitDiffis important and well-communicated.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/handler/BitbucketCloudBranchWebhookHandler.java (2)
94-103: Excellent PR number extraction logic!The implementation correctly handles the PR merge scenario by:
- Checking for the specific event type and non-null pullRequestId
- Safely parsing the PR number with appropriate error handling
- Providing clear logging for both success and failure cases
The comments effectively explain why PR diff is preferred over merge commit diff for complete file coverage.
105-106: Good logging enhancement!Adding
sourcePrNumberto the log statement improves observability and helps trace PR-based analysis flows. The enhanced context will be valuable for debugging and monitoring.
| // Model not found/invalid | ||
| if (lowerMessage.contains("model") && (lowerMessage.contains("not found") || | ||
| lowerMessage.contains("invalid") || lowerMessage.contains("does not exist"))) { | ||
| return "The configured AI model is not available. " + | ||
| "Please contact your administrator to update the AI connection settings."; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overly broad "model" matching may misclassify non-AI errors.
The condition lowerMessage.contains("model") is too permissive—it will match unrelated errors like "data model validation failed" or "model.json not found", leading to incorrect remediation advice.
Consider adding AI-provider context to narrow the match:
🔎 Proposed fix
// Model not found/invalid
- if (lowerMessage.contains("model") && (lowerMessage.contains("not found") ||
- lowerMessage.contains("invalid") || lowerMessage.contains("does not exist"))) {
+ if ((lowerMessage.contains("ai model") || lowerMessage.contains("llm model") ||
+ lowerMessage.contains("openai") || lowerMessage.contains("anthropic") ||
+ lowerMessage.contains("gemini") || lowerMessage.contains("ollama")) &&
+ (lowerMessage.contains("model") && (lowerMessage.contains("not found") ||
+ lowerMessage.contains("invalid") || lowerMessage.contains("does not exist")))) {
return "The configured AI model is not available. " +
"Please contact your administrator to update the AI connection settings.";
}Alternatively, check for known AI provider model error patterns (e.g., model_not_found, invalid_model_id).
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookAsyncProcessor.java
around lines 420 to 425, the current check uses lowerMessage.contains("model")
which is too broad and misclassifies non-AI errors; refine the match by
restricting to AI-provider model error indicators — update the condition to look
for provider-specific keywords or patterns (e.g., "model_not_found",
"invalid_model_id", "model does not exist", "No such model", "openai: model",
"azure: model", "gpt-") or require both "model" and a provider token (like
"openai" or "gpt") before returning the AI-model remediation message;
alternatively build a small list/regex of known AI error codes and use that to
decide when to return the configured-model-not-available message.
| @Override | ||
| public String getPullRequestDiff(OkHttpClient client, String owner, String repoSlug, String prNumber) throws IOException { | ||
| GetPullRequestDiffAction action = new GetPullRequestDiffAction(client); | ||
| return action.getPullRequestDiff(owner, repoSlug, Integer.parseInt(prNumber)); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PR diff method added; consider handling invalid PR number format.
The implementation follows the established pattern. However, Integer.parseInt(prNumber) at line 34 can throw an unchecked NumberFormatException if prNumber is not a valid integer. While the interface declares throws IOException, a malformed PR number would throw a different exception type.
Consider either validating the input or documenting this behavior.
🔎 Option: Wrap parse exception
@Override
public String getPullRequestDiff(OkHttpClient client, String owner, String repoSlug, String prNumber) throws IOException {
GetPullRequestDiffAction action = new GetPullRequestDiffAction(client);
- return action.getPullRequestDiff(owner, repoSlug, Integer.parseInt(prNumber));
+ try {
+ return action.getPullRequestDiff(owner, repoSlug, Integer.parseInt(prNumber));
+ } catch (NumberFormatException e) {
+ throw new IOException("Invalid PR number format: " + prNumber, e);
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| public String getPullRequestDiff(OkHttpClient client, String owner, String repoSlug, String prNumber) throws IOException { | |
| GetPullRequestDiffAction action = new GetPullRequestDiffAction(client); | |
| return action.getPullRequestDiff(owner, repoSlug, Integer.parseInt(prNumber)); | |
| } | |
| @Override | |
| public String getPullRequestDiff(OkHttpClient client, String owner, String repoSlug, String prNumber) throws IOException { | |
| GetPullRequestDiffAction action = new GetPullRequestDiffAction(client); | |
| try { | |
| return action.getPullRequestDiff(owner, repoSlug, Integer.parseInt(prNumber)); | |
| } catch (NumberFormatException e) { | |
| throw new IOException("Invalid PR number format: " + prNumber, e); | |
| } | |
| } |
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/github/service/GitHubOperationsService.java
around lines 31 to 35, the call Integer.parseInt(prNumber) can throw
NumberFormatException which is not declared by the method signature; catch
NumberFormatException around the parse, validate the prNumber first or parse
inside a try/catch, and on failure throw an IOException (or another declared
checked exception) with a clear message including the invalid prNumber and the
original exception as the cause so the method contract is honored and callers
receive a consistent checked exception.
| # Google AI provider (Gemini models) | ||
| # langchain-google-genai >= 4.0.0 automatically handles thought signatures | ||
| if provider == "google": | ||
| model_lower = ai_model.lower() | ||
| is_gemini_3 = "gemini-3" in model_lower or "gemini3" in model_lower | ||
|
|
||
| if is_gemini_3: | ||
| # Gemini 3 models use thinking_level parameter | ||
| # "minimal" reduces latency while preserving thought signatures for tool calls | ||
| # Temperature defaults to 1.0 for Gemini 3 per Google's recommendation | ||
| return ChatGoogleGenerativeAI( | ||
| google_api_key=ai_api_key, | ||
| model=ai_model, | ||
| temperature=temperature if temperature > 0 else 0.1, | ||
| thinking_level="minimal", | ||
| ) | ||
| else: | ||
| # Gemini 2.x models use thinking_budget parameter | ||
| # thinking_budget=0 disables thinking for faster responses | ||
| return ChatGoogleGenerativeAI( | ||
| google_api_key=ai_api_key, | ||
| model=ai_model, | ||
| temperature=temperature, | ||
| thinking_budget=0, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
langchain-google-genai ChatGoogleGenerativeAI thinking_level thinking_budget 4.0.0
💡 Result:
Summary — ChatGoogleGenerativeAI (langchain-google-genai 4.0.0)
- Introduced in langchain-google-genai 4.0.0 and supports both Gemini Developer API and Vertex AI backends. [1][2]
- thinking_level (for Gemini 3+): accepts 'low' or 'high' (Gemini 3+ defaults to 'high'); controls reasoning depth/latency/cost and takes precedence over thinking_budget. Use include_thoughts=True to return reasoning. [1][2]
- thinking_budget (for Gemini 2.5): integer token budget; 0 disables thinking (where supported), -1 enables dynamic thinking, positive integer constrains tokens. Deprecated for Gemini 3+; if both provided, thinking_level wins. [1][2]
- Example usage (constructor parameters): model="gemini-3-pro-preview", thinking_level="low" or model="gemini-2.5-flash", thinking_budget=1024. [1][2]
Sources:
[1] LangChain reference — ChatGoogleGenerativeAI (langchain-google-genai).
[2] LangChain docs — ChatGoogleGenerativeAI / thinking support (langchain-google-genai).
🌐 Web query:
langchain-google-genai ChatGoogleGenerativeAI API documentation parameters
💡 Result:
Key constructor / invocation parameters for LangChain's ChatGoogleGenerativeAI (summary):
- model — model name (e.g., gemini-1.5-pro, gemini-3-pro). [1][2]
- google_api_key / GOOGLE_API_KEY env var — API key (or provide credentials/project to use Vertex AI). [2][3]
- vertexai (or auto-detect via GOOGLE_GENAI_USE_VERTEXAI / project / credentials) — choose Vertex AI backend. [2]
- temperature — sampling temperature (Gemini 3.x+ defaults noted in docs). [2]
- max_output_tokens / max_tokens (or max_tokens=None) — output token limit. [1][2]
- timeout — network call timeout. [2]
- max_retries — retry count for calls. [2]
- streaming / token-level streaming support — enables token streaming. [1][3]
- input types supported — text, images, audio, video (model-dependent). [1][3]
- safety/settings / response_metadata — safety checks and metadata available on responses. [1][2]
- proxy support via HTTPS_PROXY env var. [2]
For the complete, up-to-date list (full signatures and all optional args), see the API reference pages: LangChain JS/Python docs and reference. [1][2][3]
Sources:
[1] LangChain JS integration page — ChatGoogleGenerativeAI.
[2] LangChain Python API reference — ChatGoogleGenerativeAI.
[3] LangChain docs (overview / instantiation & backend selection).
🏁 Script executed:
# Check if there's any documentation or source code in the repo about these parameters
fd -e py -e md . | xargs grep -l "thinking_level\|thinking_budget" 2>/dev/null | head -20Repository: rostilos/CodeCrow
Length of output: 107
Critical: Invalid thinking_level parameter value for Gemini 3.
The code at line 195 uses thinking_level="minimal", but langchain-google-genai 4.0.0 only accepts 'low' or 'high' for the thinking_level parameter. This will cause a runtime error. Change to thinking_level="low" to reduce latency while enabling thinking for Gemini 3 models.
Additionally, the temperature override at line 199 (temperature if temperature > 0 else 0.1) overrides user preference when temperature=0.0 is explicitly set, which may be undesirable for deterministic code review scenarios. Consider removing this override and letting the provider's default handle it.
🧰 Tools
🪛 GitHub Check: CodeCrow Analysis
[notice] 190-190: LOW severity issue
Gemini 3 models override temperature <=0 to 0.1 ("temperature=temperature if temperature > 0 else 0.1"), reducing determinism/consistency for code review/analysis where temperature=0.0 preferred.
🤖 Prompt for AI Agents
In python-ecosystem/mcp-client/llm/llm_factory.py around lines 186 to 210, the
Gemini 3 branch uses an invalid thinking_level="minimal" and forcibly overrides
temperature to 0.1 when temperature == 0.0; change thinking_level to "low"
(langchain-google-genai accepts "low" or "high") and remove the temperature
override so the temperature argument is passed through unchanged (allowing
explicit 0.0 for deterministic behavior or letting the provider default if
caller omits it).
| langchain-core>=1.0.0,<2.0.0 | ||
| langchain-openai>=1.0.0,<2.0.0 | ||
| langchain-anthropic>=1.0.0,<2.0.0 | ||
| langchain-google-genai>=4.0.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
langchain-core 1.0.0 latest version PyPI 2025
💡 Result:
As of today (Dec 29, 2025): the PyPI package langchain-core's latest release is 1.2.5 (uploaded Dec 22, 2025). LangChain 1.0.0 was released/announced on Oct 22, 2025. [1][2]
Sources:
[1] PyPI — langchain-core project page (shows 1.2.5 upload Dec 22, 2025).
[2] LangChain changelog announcement — "LangChain 1.0 now generally available" (Oct 22, 2025).
🌐 Web query:
langchain-openai 1.0.0 latest version PyPI
💡 Result:
No — the latest on PyPI is langchain-openai 1.1.6 (uploaded Dec 18, 2025). [1]
[1] PyPI: langchain-openai release files and upload dates. (pypi.org)
Citations:
🌐 Web query:
langchain-anthropic 1.0.0 latest version PyPI
💡 Result:
The latest PyPI release of langchain-anthropic is 1.3.0 (uploaded Dec 12, 2025). [1][2]
Install: pip install langchain-anthropic==1.3.0.
Sources: PyPI project page [1], package summary (Safety DB) [2].
🌐 Web query:
langchain-google-genai version PyPI upper bound recommended
💡 Result:
Use a major-version upper bound. For example, with the current PyPI release (4.1.2, Dec 19, 2025) pin like:
pip: "langchain-google-genai>=4.1.2,<5.0.0". [1]
If you need a 2.x line instead, use "<3.0.0" (e.g., 2.1.3). [2]
Sources: PyPI release pages for langchain-google-genai (latest and 2.1.3). [1] [2]
References
[1] https://pypi.org/project/langchain-google-genai/ (release 4.1.2).
[2] https://pypi.org/project/langchain-google-genai/2.1.3/.
Add upper bound constraint for langchain-google-genai.
langchain-google-genai>=4.0.0 lacks an upper bound. Add <5.0.0 to prevent breakage when version 5.x is released: langchain-google-genai>=4.0.0,<5.0.0. This aligns with the constraint pattern used for the other LangChain packages.
The jump to LangChain 1.x versions is appropriate—these are the current stable releases as of October 2025 and are already in use across the ecosystem.
🤖 Prompt for AI Agents
In python-ecosystem/mcp-client/requirements.txt around lines 7 to 10, the
dependency langchain-google-genai is missing an upper bound which risks breaking
changes when v5.x is released; update its constraint to
langchain-google-genai>=4.0.0,<5.0.0 to match the pattern used by the other
langchain packages and prevent automatic upgrade to major version 5.
| "google/gemini-3-flash-preview": 1000000, | ||
| "google/gemini-3-pro-preview": 1000000, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Google Gemini 3 Flash Preview context window token limit specifications
💡 Result:
Gemini 3 Flash (gemini-3-flash-preview) — input context window: 1,048,576 tokens (≈1M); maximum output tokens: 65,536 (≈64K). [1][2]
Sources:
[1] Google AI — Gemini 3 model docs.
[2] Google Cloud (Vertex AI) — Gemini 3 Flash model page.
🌐 Web query:
Google Gemini 3 Pro Preview context window size tokens official documentation
💡 Result:
Gemini 3 Pro (preview) official docs: input context up to 1,048,576 tokens (~1.048M) and output limit up to 65,536 tokens. [1][2][3]
Sources:
[1] Gemini 3 Developer Guide (Google AI).
[2] Gemini models — Gemini API (Google AI).
[3] Gemini 3 Pro — Vertex AI (Google Cloud).
Update token limits to match official Google specifications: 1,048,576 tokens.
The code currently sets both preview models to 1,000,000 tokens, but official Google documentation specifies the input context window as 1,048,576 tokens for both Gemini 3 Flash Preview and Gemini 3 Pro Preview. Update lines 86-87 to use the correct limit of 1048576.
🤖 Prompt for AI Agents
In python-ecosystem/mcp-client/utils/context_builder.py around lines 86 to 87,
the token limits for "google/gemini-3-flash-preview" and
"google/gemini-3-pro-preview" are set to 1000000 but should be updated to the
official Google value of 1048576; change both values from 1000000 to 1048576 so
the mapping matches the documented input context window.
| # Model not found/invalid | ||
| if "model" in error_lower and any(term in error_lower for term in ["not found", "invalid", "does not exist", "unavailable"]): | ||
| return ( | ||
| "The configured AI model is not available. " | ||
| "Please contact your administrator to update the AI connection settings." | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Broad model check may misclassify unrelated errors.
The condition "model" in error_lower and any(term in error_lower for term in ["not found", "invalid", ...]) can falsely match errors like "database model not found" or "config model file invalid", providing misleading user guidance about AI settings.
Consider adding more specific patterns like "ai model" or "llm model" to reduce false positives.
🔎 Suggested improvement
# Model not found/invalid
- if "model" in error_lower and any(term in error_lower for term in ["not found", "invalid", "does not exist", "unavailable"]):
+ if any(prefix in error_lower for prefix in ["ai model", "llm model", "gpt", "claude", "gemini"]) and any(term in error_lower for term in ["not found", "invalid", "does not exist", "unavailable"]):
return (
"The configured AI model is not available. "
"Please contact your administrator to update the AI connection settings."
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Model not found/invalid | |
| if "model" in error_lower and any(term in error_lower for term in ["not found", "invalid", "does not exist", "unavailable"]): | |
| return ( | |
| "The configured AI model is not available. " | |
| "Please contact your administrator to update the AI connection settings." | |
| ) | |
| # Model not found/invalid | |
| if any(prefix in error_lower for prefix in ["ai model", "llm model", "gpt", "claude", "gemini"]) and any(term in error_lower for term in ["not found", "invalid", "does not exist", "unavailable"]): | |
| return ( | |
| "The configured AI model is not available. " | |
| "Please contact your administrator to update the AI connection settings." | |
| ) |
🧰 Tools
🪛 GitHub Check: CodeCrow Analysis
[warning] 47-47: MEDIUM severity issue
Broad check 'model' in error_lower && any('not found'|'invalid'|...) falsely classifies unrelated errors (e.g., 'config model file not found', 'database model invalid') as AI model issues, providing misleading user guidance.
🤖 Prompt for AI Agents
In python-ecosystem/mcp-client/utils/error_sanitizer.py around lines 43 to 48,
the current heuristic checks for the substring "model" with generic terms like
"not found" or "invalid", which can misclassify unrelated errors (e.g., database
or config model issues). Narrow the match to AI-specific phrases by requiring
tokens such as "ai model", "llm", "language model", "openai model", "gpt", or
"model: openai" (and their plural/spacing variants), and/or anchor with context
words like "connection", "deployment", or "provider"; only when one of these
AI-specific patterns is present alongside the error terms return the
AI-model-not-available message. Ensure matching is case-insensitive and keep the
original fallback behavior for other errors.
| # If it looks safe, return a cleaned version | ||
| # Remove any potential API keys or tokens | ||
| sanitized = re.sub(r'sk-[a-zA-Z0-9]{20,}', '[API_KEY_REDACTED]', error_message) | ||
| sanitized = re.sub(r'api[_-]?key["\s:=]+["\']?[a-zA-Z0-9-_]+["\']?', '[API_KEY_REDACTED]', sanitized, flags=re.IGNORECASE) | ||
|
|
||
| return sanitized |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API key redaction patterns are incomplete.
The current regex only handles OpenAI-style sk-... keys and generic api_key=... patterns. This misses Anthropic keys (sk-ant-...), Google AI keys (AIzaSy...), and bearer tokens that could leak in error messages.
🔎 Extended redaction patterns
# If it looks safe, return a cleaned version
# Remove any potential API keys or tokens
sanitized = re.sub(r'sk-[a-zA-Z0-9]{20,}', '[API_KEY_REDACTED]', error_message)
+ # Anthropic keys
+ sanitized = re.sub(r'sk-ant-[a-zA-Z0-9\-_]{40,}', '[API_KEY_REDACTED]', sanitized)
+ # Google AI keys
+ sanitized = re.sub(r'AIzaSy[a-zA-Z0-9\-_]{33}', '[API_KEY_REDACTED]', sanitized)
+ # Bearer tokens
+ sanitized = re.sub(r'Bearer\s+[a-zA-Z0-9\-_.]+', 'Bearer [TOKEN_REDACTED]', sanitized, flags=re.IGNORECASE)
sanitized = re.sub(r'api[_-]?key["\s:=]+["\']?[a-zA-Z0-9-_]+["\']?', '[API_KEY_REDACTED]', sanitized, flags=re.IGNORECASE)
return sanitized🤖 Prompt for AI Agents
In python-ecosystem/mcp-client/utils/error_sanitizer.py around lines 136 to 141,
the current redaction only covers OpenAI-style sk- keys and simple api_key
assignments; extend the redaction to also detect and replace Anthropic keys
(e.g., sk-ant-...), Google API keys (AIzaSy...), Bearer tokens ("Bearer
<token>"), JWT-like tokens (three base64url segments separated by dots), long
hex or alphanumeric tokens (e.g., 32+ chars), and common prefixed keys (e.g.,
X-API-KEY, api-key:); implement additional re.sub calls (or a single compiled
pattern with alternatives) using re.IGNORECASE and word boundaries where
appropriate to replace matched secrets with a consistent marker like
[API_KEY_REDACTED], ensuring order avoids partial matches (more specific
patterns first) and that quoted/unquoted and assignment forms are handled.
|
/codecrow review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (3)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/chunking.py (1)
9-19: Clear deprecation documentation.The deprecation notice is well-documented, listing the specific advantages of
SemanticCodeSplitterand clarifying backward compatibility intent. Consider also adding aDeprecationWarningin__init__to provide runtime feedback for callers.🔎 Optional: Add runtime deprecation warning
import warnings class CodeAwareSplitter: """...""" def __init__(self, code_chunk_size: int = 800, code_overlap: int = 200, text_chunk_size: int = 1000, text_overlap: int = 200): warnings.warn( "CodeAwareSplitter is deprecated, use SemanticCodeSplitter instead", DeprecationWarning, stacklevel=2 ) # ... rest of initpython-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py (1)
39-51:CodeBlockdataclass is defined but unused.The
CodeBlockdataclass is never instantiated in this module._split_documentreturnsTextNodeobjects directly and_extract_metadatareturns a dictionary. Consider removing it if not planned for future use, or document its intended purpose.python-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.py (1)
2-8: Public API export looks good.
SemanticCodeSplitteris correctly added to__all__. The static analysis suggests sorting__all__alphabetically for consistency (RUF022), which is an optional style improvement.🔎 Optional: Sort __all__ alphabetically
__all__ = [ + "CodeAwareSplitter", "DocumentLoader", - "CodeAwareSplitter", "FunctionAwareSplitter", - "SemanticCodeSplitter", - "RAGIndexManager" + "RAGIndexManager", + "SemanticCodeSplitter" ]
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (6)
python-ecosystem/rag-pipeline/requirements.txtpython-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/chunking.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.pypython-ecosystem/rag-pipeline/src/rag_pipeline/utils/utils.py
🧰 Additional context used
🧬 Code graph analysis (2)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.py (1)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py (1)
SemanticCodeSplitter(132-440)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.py (1)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py (1)
SemanticCodeSplitter(132-440)
🪛 Ruff (0.14.10)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.py
2-8: __all__ is not sorted
Apply an isort-style sorting to __all__
(RUF022)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py
198-198: Do not catch blind exception: Exception
(BLE001)
🔇 Additional comments (6)
python-ecosystem/rag-pipeline/src/rag_pipeline/utils/utils.py (1)
13-13: LGTM!Adding
.phtmlto PHP language mapping is appropriate — this extension is commonly used for PHP template files in frameworks like Zend/Laminas and Magento.python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py (2)
196-201: Catching broadExceptionis intentional here for fallback robustness.The static analysis flags
BLE001for the blind exception catch, but this is appropriate here — the fallback ensures processing continues even if a specific language splitter fails unexpectedly. Consider logging the exception type for debugging.🔎 Optional: Log exception type for debugging
except Exception as e: - logger.warning(f"Splitting failed for {path}: {e}, using fallback") + logger.warning(f"Splitting failed for {path}: {type(e).__name__}: {e}, using fallback") nodes = self._fallback_split(doc)
232-236: Line number calculation may be inaccurate when chunk is not found.If
text.find(chunk, text_offset)returns-1(chunk not found, e.g., due to overlap or normalization),text_offsetremains unchanged, causing subsequent line numbers to be incorrect. Consider handling this edge case.🔎 Suggested defensive handling
# Calculate approximate line numbers start_line = text[:text_offset].count('\n') + 1 if text_offset > 0 else 1 chunk_pos = text.find(chunk, text_offset) if chunk_pos >= 0: text_offset = chunk_pos + len(chunk) + start_line = text[:chunk_pos].count('\n') + 1 end_line = start_line + chunk.count('\n')python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.py (2)
15-15: Import updated correctly.The import switch from
CodeAwareSplittertoSemanticCodeSplitteraligns with the new semantic splitter module.
48-52: Splitter initialization updated appropriately.The
SemanticCodeSplitteris initialized with sensible parameters:
max_chunk_sizefrom configmin_chunk_sizeasmin(200, config.chunk_size // 4)— a reasonable heuristicoverlapfrom configThis aligns with the new splitter's constructor signature.
python-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.py (1)
12-12: Import wiring is correct.The new import properly makes
SemanticCodeSplitteravailable for external consumers of this package.
| # LangChain text splitters (language-aware code splitting) | ||
| langchain-text-splitters>=0.3.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat python-ecosystem/rag-pipeline/requirements.txtRepository: rostilos/CodeCrow
Length of output: 827
🌐 Web query:
langchain-text-splitters latest version and breaking changes between 0.3.0 and newer versions
💡 Result:
Latest published releases (summary)
- PyPI / release history: 0.3.0 (existing tag). (pypi.org)
- GitHub recent releases show patch-series 0.3.x up to 0.3.9 (Jul 24, 2025) with many fixes/features. (phillipring.com)
- Security/aggregate trackers indicate a 1.0/1.1.x line exists (safetydb lists 1.1.0 as the latest safe release dated Dec 14, 2025). If you need the absolute latest (after Dec 14, 2025) I can re-check PyPI/GitHub now. (data.safetycli.com)
Breaking / notable changes since 0.3.0 (high‑level)
- Peer dependency and compatibility: packages moved to rely on langchain-core/@langchain/core as a peer dependency (you may need to explicitly install a compatible core package). This can require aligning langchain / langchain-core versions. (js.langchain.com)
- Runtime/behavior changes in callbacks: callbacks were made non‑blocking/background by default (serverless code may need to await them). (js.langchain.com)
- Pydantic / Python compatibility: LangChain ecosystem moved to Pydantic v2 and dropped older Python versions in the 0.3 series — affects models/validation if your code relied on Pydantic v1 behavior. (python.langchain.com)
- API/feature changes inside text‑splitters (0.3.x patches): security hardening for HTML/XML parsing (removed unsafe XSLT paths, hardened XML parsers), added validations (chunk_size / chunk_overlap), new args (e.g., keep_separator for HTMLSemanticPreservingSplitter), fixes to header/HTML splitters and tokenization; these changes can alter splitter behavior and raise validation errors where previous inputs were permissive. (phillipring.com)
Practical compatibility notes
- If you upgrade from 0.3.0 to any 0.3.x/1.x, pin and align langchain / langchain-core versions to avoid runtime/type conflicts; test splitters that parse HTML/markdown because parsing/security changes and stricter validations can change outputs or raise exceptions. (phillipring.com)
If you want I can:
- fetch the exact changelog entries between 0.3.0 and a specific target version (e.g., 0.3.9 or 1.1.0) and list concrete breaking-change lines and code examples. Which target version should I compare to?
Citations:
- 1: https://pypi.org/project/langchain-text-splitters/0.3.0/?utm_source=openai
- 2: https://phillipring.com/langchain-ai/langchain/releases?utm_source=openai
- 3: https://data.safetycli.com/packages/pypi/langchain-text-splitters/?utm_source=openai
- 4: https://js.langchain.com/docs/versions/v0_3/?utm_source=openai
- 5: https://js.langchain.com/docs/versions/v0_3/?utm_source=openai
- 6: https://python.langchain.com/docs/versions/v0_3/?utm_source=openai
- 7: https://phillipring.com/langchain-ai/langchain/releases?utm_source=openai
- 8: https://phillipring.com/langchain-ai/langchain/releases?utm_source=openai
Add an upper bound to mitigate breaking changes.
While the project's pattern is mixed (other LlamaIndex packages also use open upper bounds), langchain-text-splitters has documented breaking changes between versions: peer dependency alignment issues with langchain-core, Pydantic v2 migration, security hardening in HTML/XML parsing with stricter validations, and API changes with new validation rules (0.3.x patches through 1.x releases). An upper bound prevents unexpected incompatibilities during dependency updates.
Add an upper bound, e.g., langchain-text-splitters>=0.3.0,<1.0.0 (or <0.4.0 for stricter constraint).
🤖 Prompt for AI Agents
In python-ecosystem/rag-pipeline/requirements.txt around lines 16 to 17, the
dependency langchain-text-splitters is pinned only with a lower bound which
allows breaking changes from future major/minor releases; update the requirement
to add an upper bound to prevent incompatible upgrades (for example change to
langchain-text-splitters>=0.3.0,<1.0.0 or stricter like <0.4.0) so the project
avoids upstream API/Pydantic/langchain-core breakages.
| # Skip very small chunks unless they're standalone | ||
| if len(chunk.strip()) < self.min_chunk_size and len(chunks) > 1: | ||
| # Try to find and merge with adjacent chunk | ||
| continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small chunks are skipped without being merged.
The comment says "Try to find and merge with adjacent chunk" but the implementation just continues without merging. Content in small chunks will be lost.
🔎 Suggested fix to preserve small chunk content
One approach is to accumulate small chunks with the next valid chunk:
nodes = []
text_offset = 0
+ pending_small_chunk = ""
for i, chunk in enumerate(chunks):
if not chunk or not chunk.strip():
continue
# Skip very small chunks unless they're standalone
if len(chunk.strip()) < self.min_chunk_size and len(chunks) > 1:
- # Try to find and merge with adjacent chunk
- continue
+ # Accumulate for merging with next chunk
+ pending_small_chunk += chunk + "\n"
+ continue
+
+ # Prepend any pending small content
+ if pending_small_chunk:
+ chunk = pending_small_chunk + chunk
+ pending_small_chunk = ""Committable suggestion skipped: line range outside the PR's diff.
| if ')' not in sig: | ||
| # Multi-line signature | ||
| idx = lines.index(line.strip()) if line.strip() in lines else -1 | ||
| if idx >= 0: | ||
| for next_line in lines[idx+1:idx+5]: | ||
| sig += ' ' + next_line.strip() | ||
| if ')' in next_line: | ||
| break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential ValueError in multi-line signature extraction.
Using lines.index(line.strip()) can raise ValueError if line.strip() doesn't exist in lines exactly (due to whitespace differences). The if line.strip() in lines check compares stripped vs non-stripped lines, which may not match.
🔎 Suggested fix using enumerate
- idx = lines.index(line.strip()) if line.strip() in lines else -1
- if idx >= 0:
- for next_line in lines[idx+1:idx+5]:
+ # Find current line index by iterating
+ idx = next((j for j, l in enumerate(lines) if l.strip() == line), -1)
+ if idx >= 0:
+ for next_line in lines[idx+1:idx+5]:Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py
around lines 366 to 373, the code uses lines.index(line.strip()) which can raise
ValueError because it compares stripped text to unstripped entries; replace this
with a robust search using enumerate to find the matching line index (compare
stripped-to-stripped) or track the current index in the outer loop, then iterate
the next few lines by position (idx+1 ... idx+5) to build the multi-line
signature and avoid relying on lines.index; ensure you handle the case where no
match is found by using a default idx = -1 check before slicing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/CreateProjectRequest.java (1)
21-22: Critical: Use@NotNullinstead of@NotBlankfor enum fields.The
@NotBlankannotation is designed forCharSequencetypes (e.g.,String) and validates that the value is not null, not empty, and contains at least one non-whitespace character. Using it on an enum field likecreationModewill cause validation errors at runtime. For enum types, use@NotNullinstead.🔎 Proposed fix
- @NotBlank(message = "Creation mode is required") + @NotNull(message = "Creation mode is required") private EProjectCreationMode creationMode; // MANUAL or IMPORTjava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/response/UpdatedUserDataResponse.java (1)
53-55: Critical bug: Setter assigns field to itself instead of parameter.The
setCompanymethod on Line 54 assignsthis.companyto itself instead of to the parametercompany. This makes the setter completely non-functional.🔎 Proposed fix
public void setCompany(String company) { - this.company = UpdatedUserDataResponse.this.company; + this.company = company; }java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/service/UserService.java (2)
51-70: Unused return value fromUserDTO.fromUser().Line 69 calls
UserDTO.fromUser(savedUser)but doesn't capture or use the returnedUserDTO. Either return it to the caller or remove the call if it's not needed.🔎 Proposed fix
If the DTO is not needed, remove the call:
User savedUser = userRepository.save(user); - UserDTO.fromUser(savedUser); }Alternatively, if the DTO should be returned, change the method signature:
-public void updateUserInformation(Long userId, UpdateUserDataRequest updateRequest) throws UserIdNotFoundException { +public UserDTO updateUserInformation(Long userId, UpdateUserDataRequest updateRequest) throws UserIdNotFoundException { User user = getUserById(userId); if (updateRequest.getEmail() != null && !updateRequest.getEmail().isEmpty()) { validateEmailNotTaken(updateRequest.getEmail(), userId); user.setEmail(updateRequest.getEmail()); } if (updateRequest.getUsername() != null && !updateRequest.getUsername().isEmpty()) { validateUsernameNotTaken(updateRequest.getUsername(), userId); user.setUsername(updateRequest.getUsername()); } if(updateRequest.getCompany() != null && !updateRequest.getCompany().isEmpty()) { user.setCompany(updateRequest.getCompany()); } User savedUser = userRepository.save(user); - UserDTO.fromUser(savedUser); + return UserDTO.fromUser(savedUser); }
72-100: Logic error: Company update condition checks for empty string.Line 89 checks
updateRequest.getCompany().isEmpty()(empty is true), which means it only updates the company when it's empty. This is inconsistent with the email and username checks on Lines 77 and 83, which verify the fields are NOT empty before updating.If the intent is to allow users to update company to a non-empty value, this should be
!updateRequest.getCompany().isEmpty().🔎 Proposed fix (if non-empty values should be allowed)
- if(updateRequest.getCompany() != null && updateRequest.getCompany().isEmpty()) { + if(updateRequest.getCompany() != null && !updateRequest.getCompany().isEmpty()) { user.setCompany(updateRequest.getCompany()); updated = true; }Additionally, Line 99 has the same unused
UserDTO.fromUser(savedUser)call as flagged in theupdateUserInformationmethod.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/service/BitbucketConnectService.java (1)
1-18: Fix the package name typo before release.The package path contains a typo:
intergrationshould beintegration(line 1). This typo is also propagated to the import statement (line 18). Since this is a release candidate for version 0.2.0, correcting this now will prevent:
- Professional/credibility concerns in the public API
- Technical debt that becomes harder to fix after release
- Breaking changes required in future versions to correct the typo
Please update:
- Line 1:
package org.rostilos.codecrow.webserver.integration.service;- Line 18:
import org.rostilos.codecrow.webserver.integration.dto.response.VcsConnectionDTO;And ensure all related files using this package path are also corrected.
♻️ Duplicate comments (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/VcsRepositoryListDTO.java (1)
1-1: Critical: Same package name typo.This file has the same typo as VcsConnectionDTO.java: "intergration" should be "integration". This confirms the typo is systematic across multiple files and must be corrected before the 0.2.0 release.
🧹 Nitpick comments (9)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/EProjectCreationMode.java (1)
3-5: LGTM! Simple and correct enum implementation.The enum correctly defines two project creation modes. The
IMPORTconstant name is valid Java (uppercase enum constants don't conflict with the lowercaseimportkeyword).Consider adding Javadoc to describe each mode's purpose for better maintainability.
Optional: Add Javadoc documentation
+/** + * Defines the mode used to create a project. + */ public enum EProjectCreationMode { + /** Project created manually by user input. */ MANUAL, + /** Project created by importing from an external source. */ IMPORT }python-ecosystem/mcp-client/service/review_service.py (3)
39-43: Consider removing the commented-out path.The JAR path update aligns with the vcs-mcp refactor. However, the commented-out old path (line 41) can be removed to keep the code clean, as version control preserves the history.
🔎 Proposed cleanup
self.default_jar_path = os.environ.get( "MCP_SERVER_JAR", - #"/var/www/html/codecrow/codecrow-public/java-ecosystem/mcp-servers/vcs-mcp/target/codecrow-vcs-mcp-1.0.jar", "/app/codecrow-vcs-mcp-1.0.jar" )
210-222: Error handling looks good; optional style improvements available.The error sanitization logic correctly separates internal logging (full error with stack trace) from user-facing messages (sanitized). This is a good security practice.
Static analysis flags two minor style improvements:
- Line 212: Use
{e!s}instead of{str(e)}for explicit conversion- Line 216: Remove
fprefix from string without placeholders🔎 Optional style improvements
- logger.error(f"Review processing failed: {str(e)}", exc_info=True) + logger.error(f"Review processing failed: {e!s}", exc_info=True) sanitized_message = create_user_friendly_error(e) error_response = ResponseParser.create_error_response( - f"Agent execution failed", sanitized_message + "Agent execution failed", sanitized_message )
380-389: LGTM with optional style improvement.The error handling correctly logs the full error, sanitizes the message for users, emits an event, and re-raises the exception (appropriate for this internal helper method).
Static analysis suggests using
{e!s}instead of{str(e)}for explicit conversion on line 382.🔎 Optional style improvement
- logger.error(f"Agent execution error: {str(e)}", exc_info=True) + logger.error(f"Agent execution error: {e!s}", exc_info=True) sanitized_message = create_user_friendly_error(e)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateRepositorySettingsRequest.java (1)
3-32: Consider adding validation annotations and Javadoc.The DTO structure is correct, but consider these optional improvements:
- Add validation annotations (e.g.,
@Size,@Patternfor URLs) to enforce constraints at the API boundary.- Add Javadoc comments to document the purpose and usage of this request DTO.
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/response/RepoSummaryDTO.java (1)
3-67: Consider Lombok to reduce boilerplate.The class uses manual getters and setters for all fields. Consider using Lombok annotations (e.g.,
@Dataor@Getter/@Setter) to reduce boilerplate code and improve maintainability. Additionally, DTOs typically benefit fromequals(),hashCode(), andtoString()methods, which Lombok's@Dataprovides automatically.Example with Lombok annotations
package org.rostilos.codecrow.webserver.vcs.dto.response; +import lombok.Data; + +@Data public class RepoSummaryDTO { private String provider; private Long connectionId; private String workspaceId; private String repositorySlug; private String repositoryId; private String defaultBranch; private String name; - - public String getProvider() { - return provider; - } - - public void setProvider(String provider) { - this.provider = provider; - } - - public Long getConnectionId() { - return connectionId; - } - - public void setConnectionId(Long connectionId) { - this.connectionId = connectionId; - } - - public String getWorkspaceId() { - return workspaceId; - } - - public void setWorkspaceId(String workspaceId) { - this.workspaceId = workspaceId; - } - - public String getRepositorySlug() { - return repositorySlug; - } - - public void setRepositorySlug(String repositorySlug) { - this.repositorySlug = repositorySlug; - } - - public String getRepositoryId() { - return repositoryId; - } - - public void setRepositoryId(String repositoryId) { - this.repositoryId = repositoryId; - } - - public String getDefaultBranch() { - return defaultBranch; - } - - public void setDefaultBranch(String defaultBranch) { - this.defaultBranch = defaultBranch; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } }java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorStatusResponse.java (1)
3-41: Consider adding standard Object methods or migrating to a record.The DTO follows standard Java bean conventions, which is good. For better maintainability, consider:
- Adding
equals(),hashCode(), andtoString()methods (or use Lombok annotations if available)- If using Java 16+, consider migrating to a record for conciseness
Example using Java record (Java 16+)
package org.rostilos.codecrow.webserver.auth.dto.response; public record TwoFactorStatusResponse( boolean enabled, String type, int remainingBackupCodes ) {}This provides immutability, built-in equals/hashCode/toString, and reduces boilerplate.
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/exception/GlobalExceptionHandler.java (1)
64-64: Optional: Consider removing outdated comments.The
// Fixed paramcomments on lines 64 and 94 appear to reference previous changes and are not related to this PR's package refactoring. While not critical, removing these comments would improve code cleanliness.Also applies to: 94-94
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/controller/AuthController.java (1)
154-154: Consider using actual user roles instead of hardcodedROLE_USER.This line uses a hardcoded role list rather than the user's actual roles from the database. The same pattern appears on Line 287 in
refreshToken. While this is pre-existing code, it could lead to incorrect authorization if users have different roles.🔎 Suggested improvement
- List<String> roles = List.of("ROLE_USER"); + List<String> roles = user.getRoles().stream() + .map(role -> role.getName().name()) + .toList();
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (144)
java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/util/DiffContentFilter.javajava-ecosystem/mcp-servers/vcs-mcp/.gitignorejava-ecosystem/mcp-servers/vcs-mcp/MCP_CORRECTNESS.MDjava-ecosystem/mcp-servers/vcs-mcp/pom.xmljava-ecosystem/mcp-servers/vcs-mcp/src/main/java/module-info.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/McpHttpServer.bakjava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/McpHttpServer.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/McpStdioServer.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/McpTools.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/BitbucketCloudException.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/BitbucketConfiguration.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/BitbucketCloudClient.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/BitbucketCloudClientAdapter.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/BitbucketCloudClientFactory.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/BitbucketCloudClientImpl.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketAccount.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketBranch.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketBranchReference.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketBranchingModel.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketBranchingModelSettings.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketLink.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketParticipant.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketProject.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketProjectBranchingModel.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketPullRequest.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketRepository.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/cloud/model/BitbucketWorkspace.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/pullrequest/diff/DiffType.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/pullrequest/diff/FileDiff.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/pullrequest/diff/PullRequestDiff.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/bitbucket/pullrequest/diff/RawDiffParser.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/filter/LargeContentFilter.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/generic/FileDiffInfo.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/generic/VcsMcpClient.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/generic/VcsMcpClientFactory.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/github/GitHubClientFactory.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/github/GitHubConfiguration.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/github/GitHubException.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/github/GitHubMcpClientImpl.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/util/TokenLimitGuard.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/resources/logback.xmljava-ecosystem/pom.xmljava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/ai/controller/AIConnectionController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/ai/dto/request/CreateAIConnectionRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/ai/dto/request/UpdateAiConnectionRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/ai/service/AIConnectionService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/AnalysisIssueController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/ProjectAnalysisController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/request/IssueStatusUpdateRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/AnalysesHistoryResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/AnalysisIssueResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/service/AnalysisService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/controller/AuthController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/controller/GoogleAuthController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/controller/TwoFactorAuthController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/ForgotPasswordRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/GoogleAuthRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/LoginRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/RefreshTokenRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/ResetPasswordRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/SignupRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/TwoFactorLoginRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/TwoFactorSetupRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/TwoFactorVerifyRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/ValidateResetTokenRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/JwtResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/ResetTokenValidationResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorEnableResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorRequiredResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorSetupResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorStatusResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/GoogleOAuthService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/PasswordResetService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/RefreshTokenService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/TwoFactorAuthService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/dto/permission/PermissionTemplateDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/dto/permission/ProjectPermissionAssignmentDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/dto/request/project/EProjectCreationMode.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/exception/GlobalExceptionHandler.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/controller/HealthCheckController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/ErrorMessageResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/BitbucketConnectController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/OAuthCallbackController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/VcsIntegrationCallbackController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/VcsIntegrationController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/request/RepoOnboardRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/InstallUrlResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/RepoOnboardResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/VcsConnectionDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/VcsRepoBindingDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/VcsRepositoryListDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/service/BitbucketConnectService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/service/OAuthStateService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/service/VcsIntegrationService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/internal/controller/InternalAnalysisController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/internal/controller/InternalIssueController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/job/controller/JobController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/ProjectController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/ProjectTokenDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/BindAiConnectionRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/BindRepositoryRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/CreateProjectRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/CreateProjectTokenRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/EProjectCreationMode.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/SetLocalMcpRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateCommentCommandsConfigRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateProjectRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateRagConfigRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateRepositorySettingsRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/response/RagIndexStatusDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/ProjectService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/ProjectTokenService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/RagIndexStatusService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/RagIndexingTriggerService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/service/ConfigurationService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/controller/UserDataController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/request/ChangePasswordRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/request/UpdateUserDataRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/response/UpdatedUserDataResponse.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/service/UserService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/controller/cloud/BitbucketCloudController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/controller/github/GitHubController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/cloud/BitbucketCloudCreateRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/github/GitHubCreateRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/response/InternalVcsConnectionDto.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/response/RepoSummaryDTO.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/service/VcsConnectionWebService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/utils/BitbucketCloudConfigHandler.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/controller/WorkspaceController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/dto/request/ChangeRoleRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/dto/request/CreateRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/dto/request/InviteRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/dto/request/RemoveMemberRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/service/WorkspaceService.javapython-ecosystem/mcp-client/Dockerfilepython-ecosystem/mcp-client/service/command_service.pypython-ecosystem/mcp-client/service/pooled_review_service.pypython-ecosystem/mcp-client/service/review_service.pypython-ecosystem/mcp-client/utils/mcp_pool.pypython-ecosystem/rag-pipeline/docs/INTEGRATION_GUIDE.mdtools/mcp-testing/mcp.jsontools/production-build.sh
💤 Files with no reviewable changes (4)
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/dto/request/project/EProjectCreationMode.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/dto/permission/ProjectPermissionAssignmentDTO.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/service/ConfigurationService.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/dto/permission/PermissionTemplateDTO.java
✅ Files skipped from review due to trivial changes (6)
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/RagIndexingTriggerService.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/CreateProjectTokenRequest.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/dto/request/ChangeRoleRequest.java
- java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/util/DiffContentFilter.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/controller/HealthCheckController.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/RepoOnboardResponse.java
🧰 Additional context used
🧬 Code graph analysis (18)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/exception/GlobalExceptionHandler.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/ErrorMessageResponse.java (1)
ErrorMessageResponse(7-24)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/utils/BitbucketCloudConfigHandler.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/cloud/BitbucketCloudCreateRequest.java (1)
BitbucketCloudCreateRequest(5-43)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/controller/UserDataController.java (5)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.java (1)
MessageResponse(3-17)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/response/UpdatedUserDataResponse.java (1)
UpdatedUserDataResponse(3-56)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/request/UpdateUserDataRequest.java (1)
UpdateUserDataRequest(6-55)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/request/ChangePasswordRequest.java (1)
ChangePasswordRequest(6-50)java-ecosystem/libs/security/src/main/java/org/rostilos/codecrow/security/service/UserDetailsImpl.java (1)
UserDetailsImpl(15-119)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/controller/GoogleAuthController.java (2)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/GoogleAuthRequest.java (1)
GoogleAuthRequest(5-23)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/JwtResponse.java (1)
JwtResponse(6-96)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/service/UserService.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/user/dto/request/UpdateUserDataRequest.java (1)
UpdateUserDataRequest(6-55)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/ProjectController.java (7)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/BindAiConnectionRequest.java (1)
BindAiConnectionRequest(3-9)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/BindRepositoryRequest.java (1)
BindRepositoryRequest(3-39)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateProjectRequest.java (1)
UpdateProjectRequest(5-32)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateRepositorySettingsRequest.java (1)
UpdateRepositorySettingsRequest(3-32)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/CreateProjectTokenRequest.java (1)
CreateProjectTokenRequest(3-27)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateRagConfigRequest.java (1)
UpdateRagConfigRequest(6-52)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/ProjectTokenDTO.java (1)
ProjectTokenDTO(7-60)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/controller/AuthController.java (10)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/JwtResponse.java (1)
JwtResponse(6-96)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/ResetTokenValidationResponse.java (1)
ResetTokenValidationResponse(3-69)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorRequiredResponse.java (1)
TwoFactorRequiredResponse(3-51)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.java (1)
MessageResponse(3-17)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/ForgotPasswordRequest.java (1)
ForgotPasswordRequest(6-26)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/RefreshTokenRequest.java (1)
RefreshTokenRequest(5-24)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/ResetPasswordRequest.java (1)
ResetPasswordRequest(6-50)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/SignupRequest.java (1)
SignupRequest(7-65)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/TwoFactorLoginRequest.java (1)
TwoFactorLoginRequest(5-28)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/ValidateResetTokenRequest.java (1)
ValidateResetTokenRequest(5-24)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/controller/cloud/BitbucketCloudController.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/cloud/BitbucketCloudCreateRequest.java (1)
BitbucketCloudCreateRequest(5-43)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/PasswordResetService.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/ResetTokenValidationResponse.java (1)
ResetTokenValidationResponse(3-69)
python-ecosystem/mcp-client/service/command_service.py (4)
python-ecosystem/mcp-client/utils/error_sanitizer.py (2)
sanitize_error_for_display(12-141)create_user_friendly_error(144-161)python-ecosystem/mcp-client/service/pooled_review_service.py (1)
_emit_event(211-217)python-ecosystem/mcp-client/service/review_service.py (1)
_emit_event(930-937)python-ecosystem/mcp-client/server/web_server.py (3)
event_callback(56-60)event_callback(125-129)event_callback(179-183)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/GoogleOAuthService.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/JwtResponse.java (1)
JwtResponse(6-96)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/VcsIntegrationController.java (2)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.java (1)
MessageResponse(3-17)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/request/RepoOnboardRequest.java (1)
RepoOnboardRequest(8-124)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/ProjectService.java (4)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/BindAiConnectionRequest.java (1)
BindAiConnectionRequest(3-9)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/BindRepositoryRequest.java (1)
BindRepositoryRequest(3-39)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateProjectRequest.java (1)
UpdateProjectRequest(5-32)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateRepositorySettingsRequest.java (1)
UpdateRepositorySettingsRequest(3-32)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/controller/TwoFactorAuthController.java (6)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.java (1)
MessageResponse(3-17)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/TwoFactorSetupRequest.java (1)
TwoFactorSetupRequest(5-17)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/request/TwoFactorVerifyRequest.java (1)
TwoFactorVerifyRequest(5-17)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorEnableResponse.java (1)
TwoFactorEnableResponse(3-41)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorSetupResponse.java (1)
TwoFactorSetupResponse(3-51)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/TwoFactorStatusResponse.java (1)
TwoFactorStatusResponse(3-41)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/OAuthCallbackController.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.java (1)
MessageResponse(3-17)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/AnalysisIssueController.java (3)
python-ecosystem/mcp-client/model/models.py (1)
IssueDTO(21-39)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/ProjectAnalysisController.java (1)
IssueStatusUpdateRequest(624-633)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/AnalysisIssueResponse.java (1)
AnalysisIssueResponse(9-37)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/workspace/controller/WorkspaceController.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.java (1)
MessageResponse(3-17)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/VcsIntegrationCallbackController.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/generic/dto/message/MessageResponse.java (1)
MessageResponse(3-17)
🪛 Ruff (0.14.10)
python-ecosystem/mcp-client/service/command_service.py
116-116: Use explicit conversion flag
Replace with conversion flag
(RUF010)
211-211: Use explicit conversion flag
Replace with conversion flag
(RUF010)
python-ecosystem/mcp-client/service/review_service.py
212-212: Use explicit conversion flag
Replace with conversion flag
(RUF010)
216-216: f-string without any placeholders
Remove extraneous f prefix
(F541)
382-382: Use explicit conversion flag
Replace with conversion flag
(RUF010)
| @@ -1,13 +1,13 @@ | |||
| package org.rostilos.codecrow.webserver.controller.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.controller; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
rg -n 'intergration' --type javaRepository: rostilos/CodeCrow
Length of output: 6175
Fix typo in package name: "intergration" should be "integration" across the entire integration package hierarchy.
The package name contains a typo affecting 13 Java files across the intergration package and all its subpackages (dto, service, controller). Fixing this now prevents a breaking change after the 0.2.0 release. The required changes span package declarations in all files and numerous imports across the codebase:
org.rostilos.codecrow.webserver.intergration.*→org.rostilos.codecrow.webserver.integration.*
Affected files: VcsRepositoryListDTO, VcsIntegrationService, VcsRepoBindingDTO, RepoOnboardResponse, VcsConnectionDTO, InstallUrlResponse, OAuthStateService, RepoOnboardRequest, BitbucketConnectService, VcsIntegrationController, VcsIntegrationCallbackController, OAuthCallbackController, BitbucketConnectController.
| @@ -1,12 +1,12 @@ | |||
| package org.rostilos.codecrow.webserver.controller.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.controller; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all occurrences of "intergration" typo in the codebase
echo "=== Searching for 'intergration' typo in Java files ==="
rg -n --type=java 'intergration' || echo "No matches found"
echo -e "\n=== Checking directory structure for intergration directory ==="
fd -t d 'intergration' || echo "No directories found"
echo -e "\n=== Searching for old package references (integration spelling) ==="
rg -n --type=java 'org\.rostilos\.codecrow\.webserver\.controller\.integration' || echo "No matches found"
echo -e "\n=== Searching for correct integration spelling ==="
rg -n --type=java 'integration' | head -30Repository: rostilos/CodeCrow
Length of output: 11963
Fix typo in package name: "intergration" → "integration".
The entire org.rostilos.codecrow.webserver.intergration package contains a spelling error. "intergration" should be "integration" in:
- Package declaration (line 1)
- All imports referencing this package (lines 5, 7-8 in this file)
This typo is systematic across the entire webserver module affecting 17+ Java files in service, DTO, and controller subdirectories, the directory structure itself, and any external imports of these classes. Correcting it will require renaming the package directory and updating all files within it.
| @@ -1,9 +1,9 @@ | |||
| package org.rostilos.codecrow.webserver.controller.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.controller; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical: Fix package name typo "intergration" → "integration".
The package name contains a spelling error: "intergration" should be "integration". This affects the package declaration (line 1) and imports (lines 5-6). Since this is a 0.2.0 release candidate targeting the main branch, fixing this typo now is critical to avoid a breaking change post-release.
Based on the AI summary, this typo appears across multiple files in the PR (controllers, services, and DTOs). All occurrences must be corrected before merging.
Run the following script to identify all files with the typo:
#!/bin/bash
# Description: Find all occurrences of "intergration" typo in Java files
# Search for the typo in package declarations, imports, and directory paths
rg -n "intergration" --type=java
# Also check directory structure
fd -t d "intergration"Also applies to: 5-6
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/VcsIntegrationCallbackController.java
(lines 1 and imports around 5-6): the package name is misspelled as
"intergration" and must be changed to "integration"; update the package
declaration and any import statements referencing
org.rostilos.codecrow.webserver.intergration.* to
org.rostilos.codecrow.webserver.integration.*, move/rename the directory path
from .../intergration/... to .../integration/ to match the package, and run the
supplied rg/fd script to find and correct all other Java files (controllers,
services, DTOs) with the same typo, then rebuild to ensure no broken imports
remain.
| @@ -1,14 +1,17 @@ | |||
| package org.rostilos.codecrow.webserver.controller.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.controller; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for "intergration" typo in package declarations and imports
rg -n "intergration" --type=java
# Search for directory structure with typo
fd -t d "intergration"Repository: rostilos/CodeCrow
Length of output: 6270
Rename "intergration" package to "integration" across the module.
The package name contains a typo: intergration instead of integration. This typo is embedded in the directory structure and appears in 13+ files across controllers, services, and DTOs with 30+ occurrences in package declarations and imports. Fixing this before the 0.2.0 release will prevent a breaking change post-release, as renaming after release would impact all consumers of this module.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/VcsIntegrationController.java
around line 1, the package segment "intergration" is misspelled and must be
renamed to "integration" across the module; fix this by renaming the directory
org/rostilos/codecrow/webserver/intergration to
org/rostilos/codecrow/webserver/integration, update the package declaration at
the top of this file and every other source/test/DTO file that imports or
declares that package, adjust all import statements and fully-qualified
references, and update any build/config/resource paths (IDE project files,
module descriptors, and CI scripts) so compilation and packaging still succeed;
after renaming, run a full build and run tests to ensure no remaining references
to "intergration" remain.
| @@ -1,4 +1,4 @@ | |||
| package org.rostilos.codecrow.webserver.dto.response.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.dto.response; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for the misspelled "intergration" package across all Java files
echo "=== Searching for 'intergration' (misspelled) ==="
rg -n "intergration" --type java -i
echo ""
echo "=== Searching for 'integration' (correct spelling) ==="
rg -n "integration" --type java -i
# Check if the specific file exists
echo ""
echo "=== Checking specific file ==="
find . -path "*intergration*" -name "*.java" 2>/dev/nullRepository: rostilos/CodeCrow
Length of output: 40438
Fix package name typo: "intergration" → "integration"
The entire package hierarchy uses the misspelled name intergration and should be corrected to integration. This affects 13+ files across services, controllers, and DTOs in the web-server module, including:
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/service/java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/controller/java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/
All files must be moved to the corrected package path and all corresponding imports updated to maintain consistency with the test packages and codebase conventions that use the correct spelling.
| @@ -1,4 +1,4 @@ | |||
| package org.rostilos.codecrow.webserver.dto.response.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.dto.response; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Verify the "intergration" typo extent and check for related issues
echo "=== Searching for 'intergration' typo in Java files ==="
rg -n --type=java 'intergration'
echo -e "\n=== Checking directory structure for typo ==="
fd -t d intergration
echo -e "\n=== Searching for imports of VcsRepoBindingDTO ==="
rg -n --type=java 'import.*VcsRepoBindingDTO'
echo -e "\n=== Checking for any references to old package path ==="
rg -n --type=java 'org\.rostilos\.codecrow\.webserver\.dto\.response\.integration'
echo -e "\n=== Listing files in the intergration directory ==="
fd -t f '.' java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/ 2>/dev/null | head -20Repository: rostilos/CodeCrow
Length of output: 8254
Critical: Fix the "intergration" typo across the entire module before release.
The package name contains a systematic spelling error: "intergration" should be "integration". This typo affects 13 files across the entire intergration module (controllers, services, and DTOs) and must be corrected before the 0.2.0 release, as package names are part of the public API and will be extremely difficult to change post-release.
The following files require coordinated fixes to their package declarations and cross-module imports:
- Controllers: VcsIntegrationController, VcsIntegrationCallbackController, OAuthCallbackController, BitbucketConnectController
- Services: VcsIntegrationService, OAuthStateService, BitbucketConnectService
- DTOs: VcsRepoBindingDTO, VcsRepositoryListDTO, VcsConnectionDTO, RepoOnboardResponse, InstallUrlResponse, RepoOnboardRequest
Rename intergration → integration in all package declarations and imports throughout the module.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/dto/response/VcsRepoBindingDTO.java
(and the entire intergration module, lines noted by reviewer), the package
segment "intergration" is misspelled and must be renamed to "integration" across
all affected files; update the package declarations in the listed controllers,
services, and DTOs (VcsIntegrationController, VcsIntegrationCallbackController,
OAuthCallbackController, BitbucketConnectController, VcsIntegrationService,
OAuthStateService, BitbucketConnectService, VcsRepoBindingDTO,
VcsRepositoryListDTO, VcsConnectionDTO, RepoOnboardResponse, InstallUrlResponse,
RepoOnboardRequest), update all import statements referencing the old package,
move/rename the directory structure from .../intergration/... to
.../integration/ to match, and run/adjust any build files, tests, and IDE
project metadata so compilation and imports resolve correctly before releasing.
| @@ -1,4 +1,4 @@ | |||
| package org.rostilos.codecrow.webserver.service.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.service; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix package name typo: "intergration" → "integration"
The package name contains a spelling error: "intergration" should be "integration". This typo affects the directory structure, import statements throughout the codebase, and overall code professionalism.
According to the AI summary, other files in this PR also use the "intergration" path, so this correction should be applied consistently across all affected files.
🔎 Proposed fix
-package org.rostilos.codecrow.webserver.intergration.service;
+package org.rostilos.codecrow.webserver.integration.service;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| package org.rostilos.codecrow.webserver.intergration.service; | |
| package org.rostilos.codecrow.webserver.integration.service; |
| @@ -1,4 +1,4 @@ | |||
| package org.rostilos.codecrow.webserver.service.integration; | |||
| package org.rostilos.codecrow.webserver.intergration.service; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how widespread the "intergration" typo is
echo "=== Files with 'intergration' typo ==="
rg -l 'intergration' --type java
echo ""
echo "=== Package declarations with typo ==="
rg -n 'package.*intergration' --type java
echo ""
echo "=== Total count of 'intergration' occurrences ==="
rg 'intergration' --type java | wc -lRepository: rostilos/CodeCrow
Length of output: 4504
Typo in package and module name: intergration should be integration.
The package name contains a spelling error that is pervasive across the entire module. This affects 13 files in the services/web-server component (service, controller, and dto packages). Correcting this requires renaming the module directory, moving all files, and updating all package declarations and import statements across the module.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/intergration/service/VcsIntegrationService.java
around line 1, the package segment "intergration" is misspelled and must be
"integration"; fix this by renaming the module directory from "intergration" to
"integration", move all affected Java files into the new directory, update every
package declaration and import statement in the 13 affected files (service,
controller, dto packages) to use
"org.rostilos.codecrow.webserver.integration.*", update any build files
(pom.xml/gradle settings), resource paths, and module descriptors referencing
the old name, run IDE refactor or git mv to preserve history, then rebuild and
run tests to ensure no remaining references to the old package name.
| @@ -1,4 +1,4 @@ | |||
| package org.rostilos.codecrow.webserver.dto.request.project; | |||
| package org.rostilos.codecrow.webserver.project.dto.request; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, locate and read the CreateProjectRequest.java file
fd -t f "CreateProjectRequest.java" --exec wc -l {} \;Repository: rostilos/CodeCrow
Length of output: 178
🏁 Script executed:
# Read the file to verify package declaration and the creationMode field annotation
fd -t f "CreateProjectRequest.java" --exec cat -n {} \;Repository: rostilos/CodeCrow
Length of output: 2516
🏁 Script executed:
# Search for old package imports to verify refactoring completeness
rg -n 'import org\.rostilos\.codecrow\.webserver\.dto\.request\.project\.' --type javaRepository: rostilos/CodeCrow
Length of output: 43
🏁 Script executed:
# Search for new package imports to confirm migration
rg -n 'import org\.rostilos\.codecrow\.webserver\.project\.dto\.request\.' --type javaRepository: rostilos/CodeCrow
Length of output: 2987
Fix annotation on creationMode field: use @NotNull instead of @NotBlank.
The creationMode field is an enum type (EProjectCreationMode), but it's annotated with @NotBlank, which is only valid for CharSequence types like String. This will cause validation failures at runtime. Change the annotation to @NotNull (line 21).
The package refactoring to org.rostilos.codecrow.webserver.project.dto.request is correctly applied, and all imports throughout the codebase have been properly updated.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/CreateProjectRequest.java
around line 21, the enum field creationMode is incorrectly annotated with
@NotBlank (which only applies to CharSequence); change the annotation to
@NotNull, update imports to use javax.validation.constraints.NotNull (or the
project's validation NotNull), and remove the unused @NotBlank import to ensure
correct runtime validation for the enum field.
| "args": [ | ||
| "-jar", | ||
| "/var/www/html/codecrow/java-bitbucket-mcp/codecrow-mcp-servers/target/codecrow-mcp-servers-1.0.jar" | ||
| "/var/www/html/codecrow/vcs-mcp/codecrow-vcs-mcp/target/codecrow-vcs-mcp-1.0.jar" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, understand the repository structure
git ls-files | grep -E "(vcs-mcp|mcp\.json)" | head -20Repository: rostilos/CodeCrow
Length of output: 2093
🏁 Script executed:
# Find the actual vcs-mcp module
fd -t d "vcs-mcp" --max-depth 3Repository: rostilos/CodeCrow
Length of output: 96
🏁 Script executed:
# Check the actual structure under vcs-mcp
find . -type d -name "*vcs-mcp*" 2>/dev/nullRepository: rostilos/CodeCrow
Length of output: 97
🏁 Script executed:
# Look for the JAR file mentioned
fd -t f "codecrow-vcs-mcp-1.0.jar"Repository: rostilos/CodeCrow
Length of output: 43
🏁 Script executed:
# Examine the mcp.json file to see the full context
cat tools/mcp-testing/mcp.json 2>/dev/null || cat $(fd -name "mcp.json" -type f)Repository: rostilos/CodeCrow
Length of output: 317
Remove the extra directory level in the JAR path.
The path /var/www/html/codecrow/vcs-mcp/codecrow-vcs-mcp/target/codecrow-vcs-mcp-1.0.jar contains a nested codecrow-vcs-mcp directory that does not match the actual module structure. The module is located at java-ecosystem/mcp-servers/vcs-mcp/ (single level), not nested. Correct the path to /var/www/html/codecrow/vcs-mcp/target/codecrow-vcs-mcp-1.0.jar.
🤖 Prompt for AI Agents
In tools/mcp-testing/mcp.json around line 7, the JAR path contains an extra
nested codecrow-vcs-mcp directory; update the path by removing the redundant
directory level so it points to
/var/www/html/codecrow/vcs-mcp/target/codecrow-vcs-mcp-1.0.jar instead of
/var/www/html/codecrow/vcs-mcp/codecrow-vcs-mcp/target/codecrow-vcs-mcp-1.0.jar.
|
/codecrow analyze |
1 similar comment
|
/codecrow analyze |
…nd PR author settings
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analytics/controller/ProjectAnalyticsController.java (1)
71-75: BothcriticalIssuesandhighIssuesare assigned the same value.Lines 72–73, 82–83, 119–120, and 214–215 all assign
stats.getHighSeverityCount()to bothcriticalIssuesandhighIssues. TheBranchStatsandAnalysisStatsclasses only providegetHighSeverityCount(),getMediumSeverityCount(), andgetLowSeverityCount()— there is no separate critical severity method. Either the stats objects need to be enhanced to track critical severity separately, or the response mapping needs to reflect that only three severity levels are supported.
♻️ Duplicate comments (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analytics/controller/ProjectAnalyticsController.java (1)
118-122: Duplicate severity assignments detected.Both code blocks assign
stats.getHighSeverityCount()to bothcriticalIssuesandhighIssues(lines 119-120, 214-215). This appears to be the same issue flagged ingetProjectSummary. Verify whether separate counts should be used or if this duplication is intentional.Also applies to: 213-217
🧹 Nitpick comments (12)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/handler/CommentCommandWebhookHandler.java (1)
568-574: Consider removing or updating the stale TODO comments.Lines 568-569 contain TODO comments about authorization that appear to be outdated now that the logic is delegated to
CommandAuthorizationService. The comments reference "workspace member" checks which may no longer reflect the actual implementation.Additionally, the
authorIdparameter on line 570 is retrieved but not directly used inisAuthorizedUser— the method instead relies onpayload.prAuthorId()andpayload.prAuthorUsername(). Verify this is intentional, as the comment author ID (authorId) differs from the PR author ID.🔎 Suggested cleanup
- // TODO: Check if user is authorized - // TODO: For now, we check if the comment author is a workspace member String authorId = payload.commentData().commentAuthorId(); if (authorId != null && !isAuthorizedUser(project, authorId, payload)) {java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.java (2)
134-174: Consider handling the case wherevcsUserIdis null inaddAllowedUser.The method uses
vcsUserIdto look up existing users (line 146). IfvcsUserIdisnull, the lookup may return unexpected results (depending on the repository implementation). Consider adding a null check or validation at the start of this method.🔎 Suggested validation
@Transactional public AllowedCommandUser addAllowedUser( Project project, String vcsUserId, String vcsUsername, String displayName, String avatarUrl, String repoPermission, boolean syncedFromVcs, String addedBy) { + + if (vcsUserId == null && vcsUsername == null) { + throw new IllegalArgumentException("Either vcsUserId or vcsUsername must be provided"); + } Optional<AllowedCommandUser> existing = allowedUserRepository .findByProjectIdAndVcsUserId(project.getId(), vcsUserId);
181-193: Consider returning a boolean or Optional fromsetUserEnabledinstead of throwing.The method throws
IllegalArgumentExceptionwhen the user is not found (line 187). While this is a valid approach, returning anOptional<AllowedCommandUser>or a boolean would allow callers to handle the "not found" case without exception handling, which may be more appropriate for a REST API context.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/service/AnalysisService.java (2)
9-9: Remove unused imports.
BranchServiceandProjectServiceare imported but never referenced in this class.Also applies to: 12-12
79-99: Consider documenting the "critical" severity mapping.The hardcoded mapping from "critical" to
IssueSeverity.HIGH(line 84) appears to be an API compatibility layer. Consider adding a comment explaining this special case, or better yet, define the mapping in a constant or configuration.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/webhook/BitbucketCloudWebhookParser.java (1)
66-72: Consider simplifying the username fallback logic for clarity.The nested
asText()fallback is functionally correct but slightly non-idiomatic. While it works as intended, consider this more explicit alternative for improved readability:🔎 Proposed refactor for clarity
JsonNode author = pullRequest.path("author"); if (!author.isMissingNode()) { prAuthorId = extractUuid(author.path("uuid")); - prAuthorUsername = author.path("username").asText( - author.path("nickname").asText(null) - ); + prAuthorUsername = author.path("username").asText(null); + if (prAuthorUsername == null) { + prAuthorUsername = author.path("nickname").asText(null); + } }java-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_allowed_command_users_table.sql (1)
25-25: Consider removing redundant index.The
idx_allowed_users_project_idindex on(project_id)is redundant. The composite indexesidx_allowed_users_project_enabledon(project_id, enabled)andidx_allowed_users_vcs_user_idon(project_id, vcs_user_id)can serve queries that filter only byproject_id. Removing this index reduces storage and write overhead.🔎 Proposed fix
-- Create indexes for efficient lookups -CREATE INDEX IF NOT EXISTS idx_allowed_users_project_id ON allowed_command_users(project_id); CREATE INDEX IF NOT EXISTS idx_allowed_users_project_enabled ON allowed_command_users(project_id, enabled); CREATE INDEX IF NOT EXISTS idx_allowed_users_vcs_user_id ON allowed_command_users(project_id, vcs_user_id); CREATE INDEX IF NOT EXISTS idx_allowed_users_vcs_username ON allowed_command_users(project_id, vcs_username);java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/github/GitHubClient.java (1)
602-622: Link header parsing may be fragile with whitespace variations.The
relcomparison at line 611 uses strict equalityrel.equals("rel=\"next\""), but the GitHub Link header may contain variations likerel="next"with different spacing. Consider usingcontainsor regex for robustness.🔎 Proposed fix
for (String link : linkHeader.split(",")) { String[] parts = link.split(";"); if (parts.length >= 2) { String rel = parts[1].trim(); - if (rel.equals("rel=\"next\"")) { + if (rel.contains("rel=\"next\"") || rel.contains("rel=next")) { String url = parts[0].trim(); // Remove < and > brackets if (url.startsWith("<") && url.endsWith(">")) { return url.substring(1, url.length() - 1); } } } }java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/project/AllowedCommandUser.java (1)
161-165: Consider removing setters for auto-managed timestamp fields.The
setCreatedAtandsetUpdatedAtmethods are exposed, but these fields are auto-managed by@CreationTimestampand@UpdateTimestamp. Removing these setters would prevent accidental misuse and clarify the intended contract.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/ProjectService.java (1)
545-545: Redundant fully-qualified class name.Line 545 uses the fully-qualified class name
org.rostilos.codecrow.webserver.project.dto.request.UpdateCommentCommandsConfigRequestbut line 41 already imports this class. Simplify to justUpdateCommentCommandsConfigRequest.🔎 Proposed fix
public Project updateCommentCommandsConfig( Long workspaceId, Long projectId, - org.rostilos.codecrow.webserver.project.dto.request.UpdateCommentCommandsConfigRequest request + UpdateCommentCommandsConfigRequest request ) {java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/AllowedCommandUserController.java (2)
184-197: Destructive operation lacks confirmation safeguard.The
DELETE /allowed-usersendpoint clears all allowed users without any confirmation mechanism. While it requires admin/owner rights, an accidental call could lock out all authorized command users. Consider requiring a confirmation parameter (e.g.,?confirm=true) or a request body acknowledgment.🔎 Proposed fix
@DeleteMapping @PreAuthorize("@workspaceSecurity.hasOwnerOrAdminRights(#workspaceSlug, authentication)") public ResponseEntity<Void> clearAllowedUsers( @PathVariable String workspaceSlug, - @PathVariable String projectNamespace + @PathVariable String projectNamespace, + @RequestParam(required = true) boolean confirm ) { + if (!confirm) { + return ResponseEntity.badRequest().build(); + } Project project = getProject(workspaceSlug, projectNamespace);
31-50: Code duplication with CommandAuthorizationService in pipeline-agent.The
AllowedCommandUserServicein web-server andCommandAuthorizationServicein pipeline-agent (per the relevant code snippets) both implement nearly identical CRUD methods for allowed users. This duplication could lead to inconsistencies over time.Consider extracting the shared CRUD logic into a common service in the core library that both modules can use, keeping only module-specific logic (e.g., authorization checks in pipeline-agent, VCS sync in web-server) in their respective services.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (48)
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/project/ProjectDTO.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/project/AllowedCommandUser.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/project/config/ProjectConfig.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/project/AllowedCommandUserRepository.javajava-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_allowed_command_users_table.sqljava-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_google_provider_key.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V10__add_branch_column_to_branch_file.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V10_migration-add-workspace-slug.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V11_add_analysis_locks_and_rag_tracking.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V12__add_vcs_integration_layer.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V13__add_name_to_ai_connection.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V14__add_comment_commands_config.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V15__create_comment_command_rate_limit_table.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V16__create_pr_summarize_cache_table.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V17__add_comment_commands_indexes.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V18__add_comment_webhook_audit_log.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V19__add_failed_incremental_count_to_rag_status.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V20__create_bitbucket_connect_installation.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V21__add_command_job_types.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V22__add_ignored_comment_job_type.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V2__add_project_namespace.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V3__add_project_token_table.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V4_add_joined_at_to_workspace_member_table.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V5__add_project_flags.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V5__add_resolved_count_to_code_analysis.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V5__add_token_limitation_to_ai_connection.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V5__remove_unnecesary_contraint_on_pr_table.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V6__add_project_files_and_branch_tables.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V7__add_branch_issue_tracking_fields.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V8__add_default_branch_to_project.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V8_default_resolved_count_on_branch.sqljava-ecosystem/libs/core/src/main/resources/db/migration/old/V9__add_source_branch_to_code_analysis.sqljava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClient.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/bitbucket/cloud/BitbucketCloudClient.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/github/GitHubClient.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/model/VcsCollaborator.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/webhook/BitbucketCloudWebhookParser.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/handler/CommentCommandWebhookHandler.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/webhook/WebhookPayload.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/github/webhook/GitHubWebhookParser.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/service/AnalysisService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analytics/controller/ProjectAnalyticsController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analytics/service/ProjectAnalyticsService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/AllowedCommandUserController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateCommentCommandsConfigRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/AllowedCommandUserService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/ProjectService.java
🧰 Additional context used
🧬 Code graph analysis (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/AllowedCommandUserService.java (1)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.java (1)
Service(31-218)
🔇 Additional comments (53)
java-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_google_provider_key.sql (1)
1-4: LGTM! Clean and safe migration.The migration correctly expands the provider key constraint to include GOOGLE. The use of
DROP CONSTRAINT IF EXISTSmakes it idempotent and safe to run. The approach of dropping and recreating the CHECK constraint is standard for PostgreSQL when modifying constraint definitions.The database constraint values (OPENAI, OPENROUTER, ANTHROPIC, GOOGLE) align perfectly with the
AIProviderKeyenum in the application code.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analytics/service/ProjectAnalyticsService.java (6)
85-106: LGTM!The helper methods appropriately delegate to the underlying services and provide clean abstractions for retrieving analysis history and branch statistics.
108-165: LGTM!The trend calculation logic is well-implemented with proper edge case handling (empty data, insufficient data points, division by zero). The 10% change threshold is reasonable and clearly documented.
167-178: LGTM!The new public API methods provide clean abstractions for accessing branch issues, project statistics, and latest analysis. The use of
OptionalandorElsefor handling missing branches is appropriate.
222-251: LGTM!The
ResolvedTrendPointDTO is well-structured with appropriate fields, constructors, and accessors following standard Java conventions.
253-313: LGTM!The
BranchIssuesTrendPointDTO maintains its correct structure. The formatting changes improve readability without affecting functionality.
29-37: No issues found. The removedCodeAnalysisIssueRepositoryandBranchIssueRepositorydependencies are not referenced inProjectAnalyticsService, and all functionality has been properly migrated to use the injected services. Methods likegetBranchIssues()now delegate throughBranchServicerather than directly accessing repositories, confirming the migration is complete.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/handler/CommentCommandWebhookHandler.java (2)
18-19: LGTM! Clean dependency injection for the new authorization service.The
CommandAuthorizationServiceis properly injected via constructor and follows Spring best practices for dependency management.Also applies to: 58-58, 71-71, 81-81
595-610: LGTM! Authorization delegation is well-implemented.The method correctly delegates to
CommandAuthorizationService.checkAuthorization()and includes appropriate debug/info logging for both success and failure cases. The Javadoc accurately documents the supported authorization modes.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.java (5)
31-40: LGTM! Clean service setup with constructor injection.Proper use of
@Serviceannotation and constructor-based dependency injection for the repository.
51-92: Authorization logic is sound, but consider a minor edge case.The authorization flow is well-structured with clear mode-based branching. However, when both
vcsUserIdandvcsUsernamearenull(lines 65-66), the method will still allow access inANYONEmode and deny in others — this is likely the correct behavior.One observation: in
PR_AUTHOR_ONLYmode (lines 83-88), if PR author info is missing (prAuthorIdandprAuthorUsernameare bothnull), theisPrAuthorcheck will fail and deny access. This is a safe default, but consider logging a warning when PR author info is unavailable to aid debugging.
101-110: Username comparison uses case-insensitive matching — verify this is intentional.The
isPrAuthormethod usesequalsIgnoreCasefor username comparison (line 106) but exact match for ID comparison (line 102). This is likely intentional since VCS user IDs are typically case-sensitive while usernames may vary in case. Just confirming this aligns with the expected behavior for your supported VCS providers (GitHub, Bitbucket).
112-124: Potential for duplicate authorization if user exists by both ID and username.The
checkAllowedUsersListmethod performs two separate repository calls — first byvcsUserId, then byvcsUsername. If a user record has both fields populated and enabled, this works correctly. However, if different records exist for the same person (one by ID, one by username), both would grant access.This is likely acceptable given the use case, but worth noting that the allowed users list integrity depends on not having duplicate entries for the same person under different identifiers.
210-217: LGTM! Clean record definition with factory methods.The
AuthorizationResultrecord is well-designed with descriptive factory methodsallowed()anddenied()that improve readability at call sites.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analytics/controller/ProjectAnalyticsController.java (10)
1-1: LGTM! Package restructuring is consistent.The package and import path updates correctly reflect the new analytics module structure.
Also applies to: 7-10, 13-13
24-24: LGTM! Class rename and new service field align with refactoring.The addition of
projectAnalyticsServicewhile retaininganalysisServicesuggests a separation of concerns between analytics operations and issue management operations.Also applies to: 29-29
31-41: LGTM! Constructor properly wires the new dependency.The
projectAnalyticsServiceparameter is correctly added and assigned to the instance field. Spring's dependency injection will handle instantiation.
70-70: LGTM! Service delegation migrated consistently.All analytics operations in this method now delegate to
projectAnalyticsService, which is appropriate for stats and history retrieval.Also applies to: 80-80, 86-86
114-116: LGTM! Analytics operations properly delegated.The migration to
projectAnalyticsServicefor stats, history, and trend calculations is consistent. The continued use ofanalysisService.findIssues(line 210) is appropriate, as it handles detailed issue queries rather than aggregated analytics.Also applies to: 128-128, 209-209, 211-211, 224-224
186-186: LGTM! Class reference correctly updated.The
severityRankmethod references have been properly updated to use the new class nameProjectAnalyticsController.Also applies to: 279-279
334-334: LGTM! History retrieval properly delegated.The migration to
projectAnalyticsService.getAnalysisHistoryis consistent with the analytics-focused refactoring.
351-351: LGTM! Trend endpoint properly migrated.The return type and service delegation have been updated to use
ProjectAnalyticsService.ResolvedTrendPoint, indicating that trend data types are now encapsulated within the analytics service.Also applies to: 360-361
373-373: LGTM! Branch issues trend endpoint properly migrated.The return type and service delegation consistently use
ProjectAnalyticsService.BranchIssuesTrendPoint, aligning with the analytics service encapsulation pattern.Also applies to: 382-383
391-484: LGTM! Issue mutation operations correctly use AnalysisService.The
updateIssueStatusandbulkUpdateIssueStatusmethods appropriately continue usinganalysisServicefor issue queries and status updates, as these are mutation operations rather than analytics aggregations.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/service/AnalysisService.java (3)
20-38: LGTM!Constructor injection is properly implemented, and the use of
@Transactionalat the class level is appropriate for managing consistency across issue status updates.
140-149: LGTM!The propagation of resolution status to related
BranchIssuerecords ensures consistency across the data model. The batch update withsaveAllis efficient, and comprehensive logging aids troubleshooting.
154-156: LGTM!Simple and correct delegation to the repository.
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/github/webhook/GitHubWebhookParser.java (4)
32-33: LGTM!The PR author variable declarations follow the existing pattern and are properly initialized.
61-66: LGTM!The PR author extraction from
pull_request.useris correct and follows GitHub's API structure. Proper null handling is in place.
92-98: LGTM!The PR author extraction from
issue.userfor PR comment events is correct. In GitHub's API, the issue author is the PR author for pull requests, and the code comment appropriately documents this behavior.
114-116: LGTM!The new PR author fields are correctly passed to the
WebhookPayloadconstructor.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/bitbucket/webhook/BitbucketCloudWebhookParser.java (2)
32-33: LGTM!The PR author variable declarations are consistent with the GitHub parser implementation.
106-108: LGTM!The new PR author fields are correctly passed to the
WebhookPayloadconstructor, consistent with the GitHub parser implementation.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/webhook/WebhookPayload.java (5)
31-33: LGTM!The new PR author fields are well-named and appropriately typed.
109-123: LGTM!The backwards-compatible constructor correctly delegates to the canonical constructor with
nullvalues for the new fields.
125-143: LGTM!The intermediate constructor provides good backwards compatibility for code that passes
commentDatabut not yet the PR author fields.
202-218: LGTM!The
withEnrichedPrDetailsmethod correctly propagates the PR author fields, maintaining immutability.
220-243: LGTM!The
withPrAuthormethod provides a clean fluent API for enriching payloads with PR author information while maintaining immutability.java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/project/ProjectDTO.java (1)
155-180: LGTM!The
CommentCommandsConfigDTOrecord is properly extended with the new authorization fields. ThefromConfigmethod correctly handles null config (returning all nulls) and nullauthorizationMode(falling back to default). The enum-to-string conversion is appropriate for the DTO layer.java-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_allowed_command_users_table.sql (1)
1-38: Migration structure looks good.The migration correctly uses
IF NOT EXISTSfor idempotency,ON DELETE CASCADEfor referential integrity, and includes appropriate documentation viaCOMMENT ON. The unique constraint prevents duplicate user entries per project/provider combination.java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/project/config/ProjectConfig.java (1)
289-302: LGTM!The helper methods
getEffectiveAuthorizationMode()andisPrAuthorAllowed()correctly provide null-safe access to the configuration values with sensible defaults.java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/project/AllowedCommandUserRepository.java (2)
16-106: Repository design looks good.The repository provides a comprehensive set of query methods for the authorization feature. The use of derived query methods for simple lookups and custom JPQL for bulk operations is appropriate.
55-78: No transaction context issue found.All
@Modifyingqueries that are actively used in the codebase are properly called within@Transactionalmethods:
deleteByProjectId()is called fromclearAllowedUsers()with@TransactionaldisableSyncedByProjectId()is called fromsyncFromVcs()with@TransactionalThe methods
deleteSyncedByProjectId()andupdateLastSyncedAt()are defined in the repository but are not currently used anywhere in the codebase.java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClient.java (1)
164-174: LGTM!The new
getRepositoryCollaboratorsmethod follows the established pattern of using a default implementation that throwsUnsupportedOperationException. This maintains backward compatibility while allowing provider-specific implementations to override it.java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/model/VcsCollaborator.java (1)
13-39: LGTM!The
VcsCollaboratorrecord provides a clean abstraction for repository collaborators across different VCS providers. The permission helper methods correctly cover common permission levels from both GitHub (write,admin,maintain) and Bitbucket (push,admin).java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateCommentCommandsConfigRequest.java (1)
59-68: LGTM!The
validatedAllowedCommands()method properly validates and filters the command list against the allowed set. Returning an empty list for null/empty input is a safe default.java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/bitbucket/cloud/BitbucketCloudClient.java (2)
619-662: LGTM!The
getRepositoryCollaboratorsimplementation correctly uses Bitbucket's workspace permissions API, handles pagination consistently with other methods in this class, and provides a clear error message for 403 permission errors.
677-709: LGTM!The
parseCollaboratorPermissionhelper correctly handles the Bitbucket permission response format, preferringaccount_idoveruuidfor user identification, and gracefully skips group permissions by returning null whenuserNodeis absent.java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/github/GitHubClient.java (1)
483-534: Well-structured collaborator fetching with proper pagination.The implementation correctly handles GitHub API pagination via Link headers, includes appropriate error handling for 403 (permission denied), and accumulates collaborators across pages. The null-safety check for
collabat line 522 is a good defensive measure.java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/project/AllowedCommandUser.java (1)
170-179: Good defensive practice in toString().The
toString()implementation excludes potentially sensitive fields likedisplayName,avatarUrl, andproject, which helps prevent accidental PII exposure in logs.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/AllowedCommandUserService.java (1)
38-41: WRITE_PERMISSIONS set is comprehensive.The permission set correctly covers the common write-level permissions across GitHub and Bitbucket:
write,admin,maintain,push,owner. The lowercase comparison inhasWritePermissionensures case-insensitive matching.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/ProjectService.java (1)
572-580: Authorization mode fields correctly integrated.The new
authorizationModeandallowPrAuthorfields are properly extracted with appropriate fallback logic, and the constructor call at lines 577-580 correctly includes these fields. The default oftrueforallowPrAuthoris sensible as it maintains backward compatibility.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/AllowedCommandUserController.java (1)
244-249: Good use of validation annotations on request DTO.The
AddAllowedUserRequestrecord correctly uses@NotBlankon required fields (vcsUserId,vcsUsername) while leaving optional fields (displayName,avatarUrl) without constraints. This ensures proper validation at the API boundary.
| @Table(name = "allowed_command_users", | ||
| uniqueConstraints = @UniqueConstraint( | ||
| columnNames = {"project_id", "vcs_user_id"}, | ||
| name = "uk_allowed_command_user_project_vcs" | ||
| ), | ||
| indexes = { | ||
| @Index(name = "idx_allowed_cmd_user_project", columnList = "project_id"), | ||
| @Index(name = "idx_allowed_cmd_user_vcs_id", columnList = "vcs_user_id") | ||
| } | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the migration file for the actual unique constraint definition
fd -t f -e sql "V0.2.0__add_allowed_command_users" --exec cat {}Repository: rostilos/CodeCrow
Length of output: 2460
Unique constraint in entity is incomplete - missing vcs_provider column.
The entity's @UniqueConstraint includes only project_id and vcs_user_id, but the migration defines the constraint as (project_id, vcs_provider, vcs_user_id). This mismatch creates data integrity risk: the same VCS user ID from different providers (e.g., GitHub ID "123" vs Bitbucket ID "123") could incorrectly trigger constraint violations. Add vcs_provider to columnNames:
@UniqueConstraint(
columnNames = {"project_id", "vcs_provider", "vcs_user_id"},
name = "uk_allowed_command_user_project_vcs"
)🤖 Prompt for AI Agents
In
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/project/AllowedCommandUser.java
around lines 19 to 28, the @UniqueConstraint currently lists only project_id and
vcs_user_id which mismatches the DB migration; update the
UniqueConstraint.columnNames to include "vcs_provider" so it becomes project_id,
vcs_provider, vcs_user_id (keep the same constraint name), and ensure any
related indexes or annotations remain consistent with this change.
| * @param authorizationMode Controls who can execute commands (default: REPO_WRITE_ACCESS) | ||
| * @param allowPrAuthor If true, PR author can always execute commands regardless of mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Javadoc references non-existent authorization mode.
The Javadoc mentions REPO_WRITE_ACCESS as the default, but the CommandAuthorizationMode enum (lines 215-219) only contains ANYONE, ALLOWED_USERS_ONLY, and PR_AUTHOR_ONLY. The actual default is ANYONE (line 245).
🔎 Proposed fix
- * @param authorizationMode Controls who can execute commands (default: REPO_WRITE_ACCESS)
+ * @param authorizationMode Controls who can execute commands (default: ANYONE)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * @param authorizationMode Controls who can execute commands (default: REPO_WRITE_ACCESS) | |
| * @param allowPrAuthor If true, PR author can always execute commands regardless of mode | |
| * @param authorizationMode Controls who can execute commands (default: ANYONE) | |
| * @param allowPrAuthor If true, PR author can always execute commands regardless of mode |
🤖 Prompt for AI Agents
In
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/project/config/ProjectConfig.java
around lines 230-231, the Javadoc incorrectly states the default authorization
mode as REPO_WRITE_ACCESS which does not exist in the CommandAuthorizationMode
enum; update the Javadoc to reference the actual enum value ANYONE as the
default and ensure the description uses the exact enum identifiers (ANYONE,
ALLOWED_USERS_ONLY, PR_AUTHOR_ONLY) so the param text accurately matches the
enum and the default shown on line 245.
| private VcsCollaborator parseCollaborator(JsonNode node) { | ||
| if (node == null) return null; | ||
|
|
||
| String id = String.valueOf(node.get("id").asLong()); | ||
| String login = getTextOrNull(node, "login"); | ||
| String avatarUrl = getTextOrNull(node, "avatar_url"); | ||
| String htmlUrl = getTextOrNull(node, "html_url"); | ||
|
|
||
| // Get permission level - prefer role_name if available, otherwise derive from permissions object | ||
| String permission = getTextOrNull(node, "role_name"); | ||
| if (permission == null && node.has("permissions")) { | ||
| permission = derivePermissionFromObject(node.get("permissions")); | ||
| } | ||
|
|
||
| // GitHub doesn't have a separate display name, use login | ||
| return new VcsCollaborator(id, login, login, avatarUrl, permission, htmlUrl); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential NullPointerException on missing "id" field.
Line 557 calls node.get("id").asLong() without checking if the "id" field exists. If a collaborator JSON object lacks an "id" field (or it's null), this will throw NPE.
🔎 Proposed fix
private VcsCollaborator parseCollaborator(JsonNode node) {
if (node == null) return null;
- String id = String.valueOf(node.get("id").asLong());
+ if (!node.has("id") || node.get("id").isNull()) {
+ log.warn("Collaborator node missing id field, skipping");
+ return null;
+ }
+ String id = String.valueOf(node.get("id").asLong());
String login = getTextOrNull(node, "login");| /** | ||
| * Get the VCS provider for a project. | ||
| */ | ||
| private EVcsProvider getVcsProvider(Project project) { | ||
| if (project.getVcsBinding() != null) { | ||
| if (project.getVcsBinding().getVcsProvider() != null) { | ||
| return project.getVcsBinding().getVcsProvider(); | ||
| } | ||
| if (project.getVcsBinding().getVcsConnection() != null) { | ||
| return project.getVcsBinding().getVcsConnection().getProviderType(); | ||
| } | ||
| } | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the file to find the addAllowedUser method and see how it uses getVcsProvider
rg -n "addAllowedUser" java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.java -A 10 -B 2Repository: rostilos/CodeCrow
Length of output: 516
🏁 Script executed:
# Look for AllowedCommandUser class definition and constructor
fd "AllowedCommandUser" --type f -x cat {}Repository: rostilos/CodeCrow
Length of output: 31673
🏁 Script executed:
# Search for AllowedCommandUser in the codebase to find its location
fd "AllowedCommandUser.java" --type fRepository: rostilos/CodeCrow
Length of output: 164
🏁 Script executed:
fd "CommandAuthorizationService.java" --type fRepository: rostilos/CodeCrow
Length of output: 198
🏁 Script executed:
# Check if there's a different getVcsProvider in CommandAuthorizationService
rg -l "CommandAuthorizationService" --type javaRepository: rostilos/CodeCrow
Length of output: 337
🏁 Script executed:
wc -l java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.javaRepository: rostilos/CodeCrow
Length of output: 202
🏁 Script executed:
# Get the file content around the mentioned lines
sed -n '150,210p' java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.javaRepository: rostilos/CodeCrow
Length of output: 2314
getVcsProvider returns null but AllowedCommandUser.vcsProvider has a NOT NULL database constraint.
The method can return null when no VCS binding or provider is found. At line ~164, this null provider is passed to the AllowedCommandUser constructor, which accepts it without validation. However, the AllowedCommandUser entity declares vcsProvider with @Column(name = "vcs_provider", nullable = false), so when allowedUserRepository.save(user) is called, it will attempt to insert null into a NOT NULL column and raise a constraint violation at runtime. Add validation to either reject the operation or provide a fallback VCS provider before constructing the entity.
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/CommandAuthorizationService.java
around lines 195-208, getVcsProvider() can return null but
AllowedCommandUser.vcsProvider is DB NOT NULL; before constructing or saving
AllowedCommandUser (used around line ~164) validate the provider: if null either
throw a clear IllegalArgumentException/Domain-specific exception rejecting the
operation, or assign a safe fallback/default EVcsProvider value; ensure the
chosen path prevents creating the entity with a null provider and add a unit
test covering both the valid and missing-VCS cases.
| /** | ||
| * Check if the comment author is the PR author. | ||
| */ | ||
| public boolean isCommentByPrAuthor() { | ||
| if (commentData == null || prAuthorId == null) { | ||
| return false; | ||
| } | ||
| // Check by ID first (more reliable) | ||
| if (prAuthorId.equals(commentData.commentAuthorId())) { | ||
| return true; | ||
| } | ||
| // Fallback to username comparison | ||
| return prAuthorUsername != null && prAuthorUsername.equals(commentData.commentAuthorUsername()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for test coverage of isCommentByPrAuthor method
rg -n --type=java 'isCommentByPrAuthor' --max-count=50
# Find test files related to WebhookPayload
fd -name '*WebhookPayload*Test*.java' -o -name '*Test*WebhookPayload*.java'
# Search in test directories
rg -n --type=java 'WebhookPayload' 'java-ecosystem/services/pipeline-agent/src/test' --max-count=20 | head -30Repository: rostilos/CodeCrow
Length of output: 568
🏁 Script executed:
#!/bin/bash
# Find test directories
find . -type d -name test | head -5
# Find WebhookPayload test files
find . -type f -name '*WebhookPayload*Test*.java' -o -name '*WebhookPayloadTest*.java'
# Search in the pipeline-agent directory for test structure
find . -path '*/pipeline-agent/*test*' -type d | head -10
# Check for test resources
ls -la java-ecosystem/services/pipeline-agent/src/ 2>/dev/null || echo "Directory not found"Repository: rostilos/CodeCrow
Length of output: 272
🏁 Script executed:
#!/bin/bash
# Search for WebhookPayload in test directories
rg --type=java 'WebhookPayload' './java-ecosystem/tests/' -l
# Search for isCommentByPrAuthor in test files
rg --type=java 'isCommentByPrAuthor' './java-ecosystem/tests/'
# List test directory structure more comprehensively
find ./java-ecosystem/tests -type f -name '*.java' | head -20Repository: rostilos/CodeCrow
Length of output: 2436
🏁 Script executed:
#!/bin/bash
# Search for all test files in the entire repository
find . -path '*/src/test/java/*.java' -type f | grep -i 'test' | wc -l
# Look specifically in pipeline-agent for any test structure
find . -path '*/pipeline-agent/*' -type f -name '*.java' | grep -i test
# Check the pipeline-agent directory structure
find ./java-ecosystem/services/pipeline-agent -type f -name '*.java' | grep -i test
# Search everywhere for any mention of isCommentByPrAuthor
rg 'isCommentByPrAuthor' . --type=javaRepository: rostilos/CodeCrow
Length of output: 233
Add test coverage for the new authorization logic.
The isCommentByPrAuthor method implementation is sound with appropriate null guards and sensible fallback logic. However, it currently lacks test coverage. Add tests for:
- Comment author matches PR author (by ID)
- Comment author matches PR author (by username fallback)
- Comment author differs from PR author
- Edge cases with null values (commentData, prAuthorId, prAuthorUsername)
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/webhook/WebhookPayload.java
around lines 245 to 258, there is no unit test coverage for isCommentByPrAuthor;
add a new test class (or extend existing test) that verifies: 1) when
commentData.commentAuthorId equals prAuthorId the method returns true; 2) when
IDs differ but prAuthorUsername equals commentData.commentAuthorUsername the
method returns true; 3) when both ID and username differ the method returns
false; and 4) null-edge cases where commentData is null, prAuthorId is null, and
prAuthorUsername is null each produce the expected false behavior; use
appropriate builders/mocks for CommentData and assert boolean results.
| if (total > 0) { | ||
| rate = (double) resolved / (double) total; | ||
| } | ||
| String date = a.getCreatedAt() == null ? a.getUpdatedAt().toString() : a.getCreatedAt().toString(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add null safety for date handling.
Same issue as Line 73: if both createdAt and updatedAt are null, calling toString() will throw a NullPointerException.
🔎 Proposed fix
- String date = a.getCreatedAt() == null ? a.getUpdatedAt().toString() : a.getCreatedAt().toString();
+ String date = a.getCreatedAt() != null ? a.getCreatedAt().toString()
+ : (a.getUpdatedAt() != null ? a.getUpdatedAt().toString() : "");📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| String date = a.getCreatedAt() == null ? a.getUpdatedAt().toString() : a.getCreatedAt().toString(); | |
| String date = a.getCreatedAt() != null ? a.getCreatedAt().toString() | |
| : (a.getUpdatedAt() != null ? a.getUpdatedAt().toString() : ""); |
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analytics/service/ProjectAnalyticsService.java
around line 216, the ternary expression assumes either createdAt or updatedAt is
non-null and calls toString(), which can throw a NullPointerException if both
are null; change the logic to check both fields and fall back to a safe default
(e.g., an empty string or a formatted "unknown" value) when both are null, or
use Optional/Objects.toString with a default to guarantee a non-null string
result.
| * - Sync users from VCS collaborators | ||
| * - Enable/disable individual users | ||
| */ | ||
| @CrossOrigin(origins = "*", maxAge = 3600) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overly permissive CORS configuration.
@CrossOrigin(origins = "*") allows requests from any origin. For a security-sensitive endpoint managing authorization, consider restricting to specific allowed origins or configuring CORS centrally via Spring Security.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/AllowedCommandUserController.java
around line 31, the @CrossOrigin(origins = "*") annotation is overly permissive;
replace it by restricting allowed origins (e.g., read allowed origins from
configuration and set them instead of "*") or remove this controller-level
annotation and configure CORS centrally in your Spring Security configuration
(define allowed origins, methods, and headers for the endpoint(s) and load
origins from environment/props), ensuring only trusted origins can call these
authorization endpoints.
| /** | ||
| * Authorization mode controlling who can execute commands. | ||
| * Options: ANYONE, WORKSPACE_MEMBERS, ALLOWED_USERS_ONLY, PR_AUTHOR_ONLY | ||
| */ | ||
| CommandAuthorizationMode authorizationMode, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Javadoc lists non-existent authorization modes.
The Javadoc mentions WORKSPACE_MEMBERS and ALLOWED_USERS_ONLY as options, but the CommandAuthorizationMode enum only contains ANYONE, ALLOWED_USERS_ONLY, and PR_AUTHOR_ONLY. Update the documentation to reflect the actual enum values.
🔎 Proposed fix
/**
* Authorization mode controlling who can execute commands.
- * Options: ANYONE, WORKSPACE_MEMBERS, ALLOWED_USERS_ONLY, PR_AUTHOR_ONLY
+ * Options: ANYONE, ALLOWED_USERS_ONLY, PR_AUTHOR_ONLY
*/
CommandAuthorizationMode authorizationMode,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * Authorization mode controlling who can execute commands. | |
| * Options: ANYONE, WORKSPACE_MEMBERS, ALLOWED_USERS_ONLY, PR_AUTHOR_ONLY | |
| */ | |
| CommandAuthorizationMode authorizationMode, | |
| /** | |
| * Authorization mode controlling who can execute commands. | |
| * Options: ANYONE, ALLOWED_USERS_ONLY, PR_AUTHOR_ONLY | |
| */ | |
| CommandAuthorizationMode authorizationMode, |
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/dto/request/UpdateCommentCommandsConfigRequest.java
around lines 47 to 51, the Javadoc lists authorization options that don't exist
in the enum (mentions WORKSPACE_MEMBERS and ALLOWED_USERS_ONLY) — update the
comment to match the actual CommandAuthorizationMode enum values (ANYONE,
ALLOWED_USERS_ONLY, PR_AUTHOR_ONLY) by removing the nonexistent option and
ensuring the listed options exactly mirror the enum.
| EVcsProvider provider = getVcsProvider(project); | ||
|
|
||
| AllowedCommandUser user = new AllowedCommandUser(project, provider, vcsUserId, vcsUsername); | ||
| user.setDisplayName(displayName); | ||
| user.setAvatarUrl(avatarUrl); | ||
| user.setRepoPermission(repoPermission); | ||
| user.setSyncedFromVcs(syncedFromVcs); | ||
| user.setAddedBy(addedBy); | ||
| if (syncedFromVcs) { | ||
| user.setLastSyncedAt(OffsetDateTime.now()); | ||
| } | ||
|
|
||
| return allowedUserRepository.save(user); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential null vcsProvider may violate database constraint.
Line 108 calls getVcsProvider(project) which can return null if the project has no VCS connection. The entity at line 110 is then created with this potentially null provider, but the database column vcs_provider is marked as NOT NULL in the entity. This would cause a ConstraintViolationException at save time.
🔎 Proposed fix
EVcsProvider provider = getVcsProvider(project);
+ if (provider == null) {
+ throw new IllegalStateException("Cannot add allowed user: project has no VCS provider configured");
+ }
AllowedCommandUser user = new AllowedCommandUser(project, provider, vcsUserId, vcsUsername);| @Transactional | ||
| public SyncResult syncFromVcs(Project project, String initiatedBy) { | ||
| VcsConnection connection = getVcsConnection(project); | ||
|
|
||
| if (connection == null) { | ||
| return new SyncResult(false, 0, 0, 0, "No VCS connection available"); | ||
| } | ||
|
|
||
| try { | ||
| List<VcsCollaborator> collaborators = fetchCollaborators(connection, project); | ||
|
|
||
| // Disable all synced users first (so we can re-enable those that still exist) | ||
| allowedUserRepository.disableSyncedByProjectId(project.getId()); | ||
|
|
||
| int added = 0; | ||
| int updated = 0; | ||
|
|
||
| for (VcsCollaborator collab : collaborators) { | ||
| // Only sync users with write permissions | ||
| if (!hasWritePermission(collab.permission())) { | ||
| continue; | ||
| } | ||
|
|
||
| // Skip users without valid identifiers | ||
| if (collab.userId() == null) { | ||
| log.warn("Skipping collaborator with null userId: {}", collab.displayName()); | ||
| continue; | ||
| } | ||
|
|
||
| // Use displayName or userId as fallback if username is null | ||
| // Bitbucket deprecated usernames, some accounts only have account_id | ||
| String effectiveUsername = collab.username() != null ? collab.username() | ||
| : (collab.displayName() != null ? collab.displayName() : collab.userId()); | ||
|
|
||
| boolean exists = allowedUserRepository.existsByProjectIdAndVcsUserId( | ||
| project.getId(), collab.userId()); | ||
|
|
||
| addAllowedUser(project, collab.userId(), effectiveUsername, | ||
| collab.displayName(), collab.avatarUrl(), collab.permission(), | ||
| true, initiatedBy); | ||
|
|
||
| if (exists) updated++; | ||
| else added++; | ||
| } | ||
|
|
||
| log.info("Synced {} collaborators for project {} ({} added, {} updated)", | ||
| collaborators.size(), project.getId(), added, updated); | ||
|
|
||
| return new SyncResult(true, added, updated, collaborators.size(), null); | ||
|
|
||
| } catch (Exception e) { | ||
| log.error("Failed to sync collaborators for project {}: {}", project.getId(), e.getMessage()); | ||
| return new SyncResult(false, 0, 0, 0, e.getMessage()); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Verify disableSyncedByProjectId exists and find its definition
rg -n "disableSyncedByProjectId" --type javaRepository: rostilos/CodeCrow
Length of output: 474
🏁 Script executed:
# Find SyncResult class definition to understand what totalFetched represents
rg -n "class SyncResult|record SyncResult" --type javaRepository: rostilos/CodeCrow
Length of output: 473
🏁 Script executed:
# Check if there's any documentation or comments about the sync behavior
rg -n "disable.*sync|soft.*delete|audit.*trail" --type java -iRepository: rostilos/CodeCrow
Length of output: 697
🏁 Script executed:
# Check the addAllowedUser method signature and implementation
rg -A 15 "void addAllowedUser" --type java java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/AllowedCommandUserService.javaRepository: rostilos/CodeCrow
Length of output: 43
🏁 Script executed:
# Look at the full syncFromVcs method to confirm the added/updated tracking logic
sed -n '160,213p' java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/service/AllowedCommandUserService.javaRepository: rostilos/CodeCrow
Length of output: 2559
🏁 Script executed:
# Check if SyncResult is documented or used in a way that explains the metric
rg -B 2 -A 5 "SyncResult" --type java java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/AllowedCommandUserController.java | head -30Repository: rostilos/CodeCrow
Length of output: 1139
totalFetched metric is misleading—it counts all fetched collaborators, not synced ones.
At line 192, SyncResult returns collaborators.size() as totalFetched, but the actual synced count is only those with write permission (lines 181–182) and valid userId (lines 185–187). Users filtered out by these checks are included in the totalFetched metric but not in added or updated, making the metric misrepresent the sync scope. The log at line 189 compounds this by labeling collaborators.size() as "Synced."
Consider renaming totalFetched to totalFetchedFromVcs and adding totalSynced (= added + updated), or document this behavior clearly.
The soft-disable pattern (line 172) is intentional and leaves disabled entries for audit trails, which is reasonable.
…status handling for previous issues
…ure correct handling of duplicate issues
… tokens and enhance connection handling
… issue deduplication; enhance prompt builder with detailed instructions
|
/codecrow analyze |
|
/codecrow analyze |
feat: Add AST-based code chunking support with new splitter and confi…
|
/codecrow analyze |
…revious issues and updating prompt instructions for unresolved issues
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 14
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
python-ecosystem/mcp-client/utils/response_parser.py (1)
69-69: Update the comment to reflect that 'id' is now a valid field.The comment states "Removes unexpected fields (like 'id')" but 'id' is now included in VALID_ISSUE_FIELDS and will be preserved, not removed.
🔎 Proposed fix
- Removes unexpected fields (like 'id') + Removes unexpected fieldsOr more explicitly:
- Removes unexpected fields (like 'id') + Keeps only valid fields (severity, category, file, line, etc.)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java (1)
427-445: PotentialNullPointerExceptionon successful response with null body.Line 433 calls
response.body().string()without null check. While unlikely for a successful OAuth token response, a null body would cause NPE. The error path (line 429) correctly handles null.🔎 Proposed fix
try (Response response = httpClient.newCall(request).execute()) { if (!response.isSuccessful()) { String errorBody = response.body() != null ? response.body().string() : ""; throw new IOException("Failed to refresh token: " + response.code() + " - " + errorBody); } - String responseBody = response.body().string(); + if (response.body() == null) { + throw new IOException("Empty response body from token endpoint"); + } + String responseBody = response.body().string(); JsonNode json = objectMapper.readTree(responseBody);java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.java (1)
321-330: Add null safety for branchName in branch analysis path.Line 323 retrieves
branchNamefrom eithertargetBranch()orsourceBranch()without null validation before passing it tocreateBranchAnalysisJob()at line 326. If either method returns null (e.g., due to malformed webhook payload or parsing issues), this will cause downstream issues.🔎 Proposed fix with null check
} else if (payload.isPushEvent() || isPrMergeEvent) { // Push event or PR merge - branch analysis String branchName = isPrMergeEvent ? payload.targetBranch() : payload.sourceBranch(); + if (branchName == null) { + log.warn("Branch name is null for event type: {}, cannot create branch analysis job", payload.eventType()); + throw new IllegalStateException("Branch name is required for branch analysis"); + } return jobService.createBranchAnalysisJob( project, branchName, payload.commitHash(), JobTriggerSource.WEBHOOK, null );
♻️ Duplicate comments (4)
java-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_allowed_command_users_table.sql (1)
2-17: Add a trigger to automatically update theupdated_attimestamp.As noted in the previous review, the
updated_atcolumn (line 13) has only a default value and will not be updated when records are modified. To maintain consistency with established patterns in migrations V12 (forvcs_repo_binding) and V14 (forcomment_command_config), you should add a trigger function and trigger to automatically setupdated_at = CURRENT_TIMESTAMPon each UPDATE operation.🔎 Suggested trigger implementation
Add this after line 17 (before the index creation):
-- Create trigger function to update updated_at timestamp CREATE OR REPLACE FUNCTION update_allowed_command_users_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; -- Create trigger CREATE TRIGGER trg_allowed_command_users_updated_at BEFORE UPDATE ON allowed_command_users FOR EACH ROW EXECUTE FUNCTION update_allowed_command_users_updated_at();python-ecosystem/mcp-client/utils/prompt_builder.py (1)
739-746: Unusedfull_diff_contentparameter should be removed.The
full_diff_contentparameter (line 743) is defined but never used in the method body. The prompt instructs the agent to "retrieve it via MCP tools ONLY if necessary" (line 819), confirming the parameter is unnecessary.Additionally,
rag_context(line 744) uses implicitOptionalwhich violates PEP 484.🔎 Proposed fix
@staticmethod def build_incremental_review_prompt( pr_metadata: Dict[str, Any], delta_diff_content: str, - full_diff_content: str, - rag_context: Dict[str, Any] = None, + rag_context: Optional[Dict[str, Any]] = None, structured_context: Optional[str] = None ) -> str:Also update the call site in
review_service.pyto remove thefull_diff_contentargument.python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py (2)
231-234: Small chunks are skipped without being merged.The comment says "Try to find and merge with adjacent chunk" but the implementation just continues without merging. Content in small chunks will be lost.
376-383: PotentialValueErrorin multi-line signature extraction.Using
lines.index(line.strip())can raiseValueErrorifline.strip()doesn't exist inlinesexactly (due to whitespace differences). Theif line.strip() in linescheck compares stripped vs non-stripped lines, which may not match.
🧹 Nitpick comments (16)
deployment/config/rag-pipeline/.env.sample (1)
21-21: Add documentation for the new AST splitter flag.The new
RAG_USE_AST_SPLITTERvariable enables a new feature (AST-based code chunking) but lacks explanatory comments. Given that other configuration sections document their purpose, consider adding a comment here to help users understand when/why they might want to disable this feature.🔎 Proposed improvement
# Free Plan Limits (Optional - set to 0 to disable limits) RAG_MAX_CHUNKS_PER_INDEX=70000 RAG_MAX_FILES_PER_INDEX=40000 +# AST-based Code Splitting (Optional - set to false to use simple line-based splitting) RAG_USE_AST_SPLITTER=truedeployment/config/java-shared/application.properties.sample (1)
45-47: Consider consolidating frontend URL properties.The new
codecrow.frontend.urlproperty appears to duplicate existing functionality. The configuration now has three frontend URL properties:
codecrow.frontend-url(line 24) - for OAuth redirectscodecrow.frontend.url(line 46, new) - for email templatescodecrow.email.frontend-url(line 56) - for email linksThis creates maintenance overhead and potential for configuration drift. Additionally, the comment on line 56 references
codecrow.frontend-url, not the new property.Consider either:
- Using a single property (e.g.,
codecrow.frontend.url) for all frontend URL references- Adding clear documentation explaining why separate properties are needed
</content_end>
python-ecosystem/mcp-client/utils/response_parser.py (1)
118-124: Refine exception handling to avoid catching bare Exception.The bare
Exceptioncatch is too broad and may hide unexpected errors. Sincestr()rarely fails except for exotic types with broken__str__methods, consider either removing the try-except or catching more specific exceptions.Based on learnings from static analysis hints.
🔎 Proposed refactoring options
Option 1: Remove try-except (simplest)
- # Ensure id is a string when present (preserve mapping to DB ids) - if key == 'id': - try: - value = str(value) if value is not None else None - except Exception: - value = None + # Ensure id is a string when present (preserve mapping to DB ids) + if key == 'id': + value = str(value) if value is not None else NoneOption 2: Catch specific exceptions if needed
- # Ensure id is a string when present (preserve mapping to DB ids) - if key == 'id': - try: - value = str(value) if value is not None else None - except Exception: - value = None + # Ensure id is a string when present (preserve mapping to DB ids) + if key == 'id': + try: + value = str(value) if value is not None else None + except (TypeError, ValueError, AttributeError): + value = Nonejava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java (1)
96-96: Consider a typed response DTO for better type safety.Using
Map<String, Object>works but loses compile-time type safety. A simple record/DTO would improve maintainability and API documentation.🔎 Example typed response
public record PagedIssuesResponse( List<IssueDTO> issues, long total, int page, int pageSize ) {}Then update the return type to
ResponseEntity<PagedIssuesResponse>.Also applies to: 138-144
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java (2)
302-310: Silent fallback on decryption failure may mask security misconfigurations.If decryption fails, the code assumes the secret is stored unencrypted (legacy) and uses it as-is. However, this could also indicate:
- Corrupted encrypted data
- Wrong encryption key
- A security issue where the secret was never encrypted as intended
Consider logging at WARN level with more context or tracking legacy installations separately to ensure proper migration.
🔎 Suggested improvement
try { decryptedSecret = encryptionService.decrypt(sharedSecret); } catch (Exception e) { - // Shared secret might be stored unencrypted (old installation) - use as-is - log.warn("Could not decrypt shared secret, using as stored: {}", e.getMessage()); + // Shared secret might be stored unencrypted (old installation) - use as-is + log.warn("Could not decrypt shared secret for installation clientKey={}, using as stored. " + + "Consider migrating legacy installations. Error: {}", + installation.getClientKey(), e.getMessage()); decryptedSecret = sharedSecret; }
405-411: SameOkHttpClientinstantiation pattern as line 327.This method also creates a new
OkHttpClientinstance per invocation. Consider extracting a shared instance or using the factory for unauthenticated token endpoint calls.🔎 Suggested approach
Add a class-level shared client for token operations:
// At class level, after other fields private final OkHttpClient tokenRefreshClient = new OkHttpClient.Builder() .connectTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS) .build();Then reuse
tokenRefreshClientin bothrefreshBitbucketConnectAppConnectionandrefreshBitbucketTokenmethods.python-ecosystem/mcp-client/service/review_service.py (2)
229-240: Good error sanitization, but minor fix needed.The error sanitization properly hides technical details from users. However, line 235 has an f-string without placeholders.
🔎 Proposed fix
error_response = ResponseParser.create_error_response( - f"Agent execution failed", sanitized_message + "Agent execution failed", sanitized_message )
657-657: Remove extraneous f-string prefixes.These log statements use f-strings without any placeholders.
🔎 Proposed fix
- logger.info(f"Using INCREMENTAL analysis mode with delta diff") + logger.info("Using INCREMENTAL analysis mode with delta diff")- logger.info(f"Using FULL analysis mode with previous analysis data") + logger.info("Using FULL analysis mode with previous analysis data")- logger.info(f"Using FULL analysis mode (first review)") + logger.info("Using FULL analysis mode (first review)")Also applies to: 672-672, 681-681
python-ecosystem/mcp-client/service/issue_post_processor.py (2)
32-46: Annotate class-level constant withClassVar.The
ISSUE_KEYWORDSlist is a class-level constant that should be annotated withtyping.ClassVarto indicate it's not an instance attribute.🔎 Proposed fix
+from typing import ClassVar, Dict, Any, List, Optional, Tuple -from typing import Dict, Any, List, Optional, Tuple ... class IssuePostProcessor: ... # Similarity threshold for considering issues as duplicates SIMILARITY_THRESHOLD = 0.75 # Maximum line number drift to attempt correction MAX_LINE_DRIFT = 15 # Keywords that indicate similar issues (for grouping) - ISSUE_KEYWORDS = [ + ISSUE_KEYWORDS: ClassVar[List[str]] = [ 'hardcode', 'hardcoded', ... ]
544-568:IssueDeduplicatoraccesses private methods ofIssuePostProcessor.The
IssueDeduplicatorclass directly calls_calculate_issue_similarityand_merge_duplicate_issueswhich are private methods (prefixed with_). This tight coupling could make future refactoring harder.Consider either:
- Making these methods public/protected (remove or change underscore prefix)
- Having
IssueDeduplicatorinherit fromIssuePostProcessor- Keeping
IssueDeduplicatoras an internal helper onlyThis is not blocking since both classes are in the same module.
python-ecosystem/rag-pipeline/docs/AST_CHUNKING.md (1)
20-51: Add language specifiers to fenced code blocks.Per markdown linting rules (MD040), fenced code blocks should have a language specified. Consider adding language identifiers:
- Line 20: Use
```textor```plaintextfor the ASCII diagram- Line 139: Use
```textfor the boost calculation example- Line 177: Use
```textor```requirementsfor the dependencies list🔎 Suggested fix
-``` +```text Document → ASTCodeSplitter → TextNodes (with metadata)-``` +```text UserService.java (service file) × functions_classes × has_names × has_docstring-``` +```text langchain-community>=0.3.0Also applies to: 139-142, 177-182
python-ecosystem/rag-pipeline/requirements.txt (1)
19-21: Consider adding upper bounds to LangChain packages.Similar to the concern raised for
langchain-text-splitters, thelangchain-communityandlangchain-corepackages also lack upper bounds. These packages share the same versioning and breaking change concerns (Pydantic v2 migration, API changes). For consistency with other pinned dependencies in this file (e.g.,qdrant-client>=1.7.0,<2.0.0), consider adding upper bounds.🔎 Suggested fix
# LangChain community (for LanguageParser with tree-sitter) -langchain-community>=0.3.0 -langchain-core>=0.3.0 +langchain-community>=0.3.0,<1.0.0 +langchain-core>=0.3.0,<1.0.0python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.py (1)
385-385: Remove extraneousfprefix from strings without placeholders.These f-strings have no interpolation placeholders.
🔎 Suggested fix
- logger.info(f"Indexing successful. Performing atomic alias swap...") + logger.info("Indexing successful. Performing atomic alias swap...")- logger.info(f"Alias swap completed successfully") + logger.info("Alias swap completed successfully")Also applies to: 429-429
python-ecosystem/rag-pipeline/src/rag_pipeline/core/ast_splitter.py (3)
549-553: Use spread syntax for list concatenation.Per Python best practices, prefer
[*parent_context, node_name]overparent_context + [node_name]for clarity.🔎 Suggested fix
# If this is a class, traverse children with updated context if is_class and node_name: - new_context = parent_context + [node_name] + new_context = [*parent_context, node_name] for child in node.children: traverse(child, new_context, depth + 1)
697-704: Rename unused loop variable.The
pattern_typevariable is not used in the loop body; rename to_pattern_typeor_to indicate intentional non-use.🔎 Suggested fix
if not chunk.semantic_names: names = [] - for pattern_type, pattern in patterns.items(): + for _pattern_type, pattern in patterns.items(): matches = pattern.findall(chunk.content) names.extend(matches)Same change applies at line 980:
- for pattern_type, pattern in patterns.items(): + for _, pattern in patterns.items(): matches = pattern.findall(chunk)
746-756: Avoid ambiguous variable namel.The variable
lon line 748 is easily confused with1orI. Rename toline_contentor similar.🔎 Suggested fix
if ')' not in sig and ':' not in sig: idx = -1 - for i, l in enumerate(lines): - if l.strip() == line: + for i, ln in enumerate(lines): + if ln.strip() == line: idx = i break
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (25)
deployment/config/java-shared/application.properties.sampledeployment/config/rag-pipeline/.env.samplejava-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/processor/analysis/BranchAnalysisProcessor.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/branch/BranchIssueRepository.javajava-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_allowed_command_users_table.sqljava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/service/AnalysisService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/PasswordResetService.javapython-ecosystem/mcp-client/model/models.pypython-ecosystem/mcp-client/service/issue_post_processor.pypython-ecosystem/mcp-client/service/rag_client.pypython-ecosystem/mcp-client/service/review_service.pypython-ecosystem/mcp-client/utils/prompt_builder.pypython-ecosystem/mcp-client/utils/response_parser.pypython-ecosystem/rag-pipeline/docs/AST_CHUNKING.mdpython-ecosystem/rag-pipeline/requirements.txtpython-ecosystem/rag-pipeline/src/rag_pipeline/api/api.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/ast_splitter.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/chunking.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.pypython-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.pypython-ecosystem/rag-pipeline/src/rag_pipeline/services/query_service.py
🚧 Files skipped from review as they are similar to previous changes (2)
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/service/AnalysisService.java
- java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/processor/analysis/BranchAnalysisProcessor.java
🧰 Additional context used
🧬 Code graph analysis (2)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/PasswordResetService.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/dto/response/ResetTokenValidationResponse.java (1)
ResetTokenValidationResponse(3-69)
python-ecosystem/mcp-client/service/review_service.py (3)
python-ecosystem/mcp-client/model/models.py (1)
AnalysisMode(21-24)python-ecosystem/mcp-client/service/issue_post_processor.py (2)
IssuePostProcessor(17-533)post_process_analysis_result(571-599)python-ecosystem/mcp-client/utils/error_sanitizer.py (2)
sanitize_error_for_display(12-141)create_user_friendly_error(144-161)
🪛 markdownlint-cli2 (0.18.1)
python-ecosystem/rag-pipeline/docs/AST_CHUNKING.md
20-20: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
139-139: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
177-177: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🪛 Ruff (0.14.10)
python-ecosystem/mcp-client/utils/prompt_builder.py
743-743: Unused static method argument: full_diff_content
(ARG004)
744-744: PEP 484 prohibits implicit Optional
Convert to T | None
(RUF013)
python-ecosystem/mcp-client/utils/response_parser.py
123-123: Do not catch blind exception: Exception
(BLE001)
python-ecosystem/mcp-client/service/issue_post_processor.py
32-46: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
python-ecosystem/rag-pipeline/src/rag_pipeline/services/query_service.py
78-78: Consider moving this statement to an else block
(TRY300)
79-79: Do not catch blind exception: Exception
(BLE001)
162-162: Unused method argument: top_k
(ARG002)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.py
152-153: try-except-pass detected, consider logging the exception
(S110)
152-152: Do not catch blind exception: Exception
(BLE001)
165-165: Do not catch blind exception: Exception
(BLE001)
295-295: Do not catch blind exception: Exception
(BLE001)
346-346: zip() without an explicit strict= parameter
Add explicit value for parameter strict=
(B905)
385-385: f-string without any placeholders
Remove extraneous f prefix
(F541)
398-398: Do not catch blind exception: Exception
(BLE001)
426-426: Do not catch blind exception: Exception
(BLE001)
429-429: f-string without any placeholders
Remove extraneous f prefix
(F541)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py
205-205: Do not catch blind exception: Exception
(BLE001)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/ast_splitter.py
376-376: Consider moving this statement to an else block
(TRY300)
378-378: Do not catch blind exception: Exception
(BLE001)
399-399: Do not catch blind exception: Exception
(BLE001)
422-422: Do not catch blind exception: Exception
(BLE001)
467-467: Consider moving this statement to an else block
(TRY300)
469-469: Do not catch blind exception: Exception
(BLE001)
551-551: Consider [*parent_context, node_name] instead of concatenation
Replace with [*parent_context, node_name]
(RUF005)
699-699: Loop control variable pattern_type not used within loop body
Rename unused pattern_type to _pattern_type
(B007)
748-748: Ambiguous variable name: l
(E741)
980-980: Loop control variable pattern_type not used within loop body
Rename unused pattern_type to _pattern_type
(B007)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.py
2-9: __all__ is not sorted
Apply an isort-style sorting to __all__
(RUF022)
python-ecosystem/mcp-client/service/review_service.py
231-231: Use explicit conversion flag
Replace with conversion flag
(RUF010)
235-235: f-string without any placeholders
Remove extraneous f prefix
(F541)
401-401: Use explicit conversion flag
Replace with conversion flag
(RUF010)
657-657: f-string without any placeholders
Remove extraneous f prefix
(F541)
672-672: f-string without any placeholders
Remove extraneous f prefix
(F541)
681-681: f-string without any placeholders
Remove extraneous f prefix
(F541)
🔇 Additional comments (40)
java-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_allowed_command_users_table.sql (1)
25-31: Previous critical issue resolved.The previous critical issue regarding lines 32-33 (COMMENT ON COLUMN statements for non-existent table
comment_commands_config) has been successfully resolved. Those problematic lines have been removed from the migration.java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/branch/BranchIssueRepository.java (2)
4-5: LGTM!The pagination imports are correctly added to support the new paged query methods.
58-62: LGTM!The count methods follow the established pattern from
countUnresolvedByBranchIdand provide consistent semantics for resolved and all issues.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java (1)
1-13: Package restructuring and imports look correct.The package path change and updated service imports align with the broader package reorganization. The new
CollectionsandHashMapimports are used appropriately in the updated methods.java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java (4)
76-86: LGTM!Constructor properly injects the new
BitbucketConnectInstallationRepositorydependency for Connect App support.
257-287: LGTM!The branching logic correctly routes Connect App connections to JWT Bearer grant flow while preserving the standard OAuth refresh flow for other connections.
455-533: LGTM!The HTTP client creation methods properly use the injected
httpClientFactoryfor authenticated requests and have appropriate error handling for missing credentials.
348-360: No transaction issue: both saves are atomic within the outer@Transactionalcontext.The private method
refreshBitbucketConnectAppConnection()is called fromrefreshBitbucketConnection(), which is called from the publicrefreshToken()method marked with@Transactional(line 228). Both repository saves participate in the same transaction, so if either save fails, the entire transaction rolls back atomically.python-ecosystem/mcp-client/service/rag_client.py (1)
72-76: LGTM! Defensive guard for missing branch parameter.The early return with an empty context when
branchis not provided is consistent with the disabled RAG case and prevents unnecessary API calls. The warning log provides good observability.python-ecosystem/mcp-client/model/models.py (3)
21-25: LGTM! Well-defined analysis mode enum.The
AnalysisModeenum withFULLandINCREMENTALvalues is clear and the docstring explains the purpose of each mode.
57-57: Good backward compatibility approach with alias choices.Using
AliasChoicesto accept both"targetBranchName"and"branch"maintains backward compatibility while allowing the simpler field name.
74-78: LGTM! Incremental analysis fields are properly structured.The new fields for incremental analysis (
analysisMode,deltaDiff,previousCommitHash,currentCommitHash) are well-typed asOptionalwith sensible defaults. The field descriptions are clear.python-ecosystem/mcp-client/utils/prompt_builder.py (1)
20-78: LGTM! Comprehensive guidance constants for line number calculation and deduplication.The
LINE_NUMBER_INSTRUCTIONSandISSUE_DEDUPLICATION_INSTRUCTIONSconstants provide detailed, actionable guidance to the LLM. The examples are particularly helpful for explaining the expected behavior.python-ecosystem/mcp-client/service/review_service.py (2)
203-219: LGTM! Good addition of post-processing step.The post-processing step correctly handles line number fixing and duplicate merging. The logging of issue count changes provides useful observability into the post-processing results.
750-756: LGTM! Proper branch handling for RAG queries.The early return when
targetBranchNameis not set prevents unnecessary RAG queries and the warning log provides good observability.python-ecosystem/mcp-client/service/issue_post_processor.py (4)
99-106: Edge case in diff parsing: empty lines handling.Lines 102-106 treat empty lines (
line == '') as context lines and increment the line counter. However, in unified diffs, context lines always start with a space character. A truly empty line (no leading space) after the hunk header could indicate end of hunk or malformed diff input.Consider whether empty lines should be handled differently or if this is acceptable for your use case.
111-148: LGTM! Well-structured post-processing pipeline.The
process_issuesmethod has a clear 3-step pipeline: fix line numbers → merge duplicates → clean fix suggestions. The logging provides good visibility into the processing results.
364-411: Good similarity calculation with weighted factors.The
_calculate_issue_similaritymethod uses a well-thought-out weighted combination of:
- Keyword overlap (40%)
- Sequence similarity (30%)
- Line proximity (20%)
- Category match (10%)
This provides a balanced approach to identifying duplicate issues.
571-598: LGTM! Clean convenience function with helpful metadata.The
post_process_analysis_resultfunction provides a clean entry point and adds useful metadata (_post_processed,_original_issue_count,_final_issue_count) for debugging and observability.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.java (1)
240-250: LGTM! Efficient early exit for non-command comments.The guard clause correctly prevents Job creation for comment events that don't contain CodeCrow commands, reducing unnecessary database records and improving performance.
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/auth/service/PasswordResetService.java (1)
1-1: Package reorganization is complete with all references properly updated.The package reorganization from
org.rostilos.codecrow.webserver.service.authtoorg.rostilos.codecrow.webserver.auth.servicehas been successfully completed. A codebase search confirms no remaining references to the old package path exist, and the only file that imports PasswordResetService (AuthController.java) correctly uses the new package path along with the updated DTO import paths.python-ecosystem/rag-pipeline/src/rag_pipeline/api/api.py (1)
265-279: Early-return guard properly handles optional branch parameter.The guard clause correctly prevents
Noneor emptybranchvalues from reachingget_context_for_pr()(line 281), which expectsbranch: str. Since the method is only called after the guard check, the downstream method signature (which requires non-optionalstr) is compatible with this design. No changes needed.python-ecosystem/rag-pipeline/src/rag_pipeline/core/chunking.py (3)
8-19: Well-documented deprecation notice.The deprecation docstring clearly explains why users should migrate to
SemanticCodeSplitterand lists the benefits. This is good practice for guiding users through API evolution.
81-91: Consistent deprecation notice.The deprecation notice for
FunctionAwareSplitterfollows the same pattern asCodeAwareSplitter, providing clear migration guidance.
148-149: Improved readability.Wrapping the long keyword list improves code readability without changing behavior.
python-ecosystem/rag-pipeline/docs/AST_CHUNKING.md (1)
1-51: Comprehensive and well-structured documentation.The architecture diagram and feature comparison table clearly communicate the benefits of AST-based chunking over traditional approaches.
python-ecosystem/rag-pipeline/src/rag_pipeline/core/__init__.py (2)
2-9: Exports correctly added for new splitters.The new
SemanticCodeSplitterandASTCodeSplitterare properly exported. The current ordering logically groups deprecated splitters (CodeAwareSplitter,FunctionAwareSplitter) and new splitters together.
13-14: Imports correctly added.The imports align with the new module structure.
python-ecosystem/rag-pipeline/src/rag_pipeline/services/query_service.py (3)
31-38: Content type boosting aligns with documentation.The boost values match those documented in
AST_CHUNKING.mdand provide meaningful differentiation between chunk types.
59-81: Robust alias/collection existence check.The method correctly handles both direct collections and aliases, which is important for the alias-based indexing migration. The exception handling is appropriately defensive for this validation check.
340-377: Well-structured multi-factor score boosting.The boosting logic correctly applies:
- File path priority (service/controller vs test files)
- Content type priority (AST metadata)
- Semantic name bonus
- Docstring bonus
The final score is capped at 1.0 to prevent overflow.
python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.py (2)
53-71: Good splitter selection with environment variable control.The environment variable
RAG_USE_AST_SPLITTERprovides flexibility to toggle between AST and regex-based splitting. The logging clearly indicates which splitter is in use.
288-296: Good defensive cleanup of orphaned versioned collections.The orphan cleanup logic prevents accumulation of failed indexing attempts. The condition
self._resolve_alias_to_collection(alias_name) == coll_namecorrectly avoids deleting the currently active collection.python-ecosystem/rag-pipeline/src/rag_pipeline/core/semantic_splitter.py (3)
24-50: Well-defined data structures.The
ChunkTypeenum andCodeBlockdataclass provide clear type definitions for semantic code analysis.
54-79: Comprehensive language mapping.The
LANGUAGE_MAPcovers 25+ languages with proper mapping to LangChain'sLanguageenum.
192-208: Good fallback handling with generator pattern.The
iter_split_documentsgenerator provides memory-efficient iteration with proper exception handling and fallback behavior.python-ecosystem/rag-pipeline/src/rag_pipeline/core/ast_splitter.py (4)
34-39: ContentType enum aligns with query_service boosting.The enum values match the keys in
CONTENT_TYPE_BOOSTinquery_service.py.
242-264: Good deterministic ID generation for deduplication.The
generate_deterministic_idfunction ensures idempotent indexing by creating reproducible chunk IDs from path, content prefix, and index.
325-380: Well-designed tree-sitter language loading with caching.The dynamic import approach with caching handles the varying APIs across tree-sitter packages (e.g.,
language_php()vslanguage()).
473-576: Excellent breadcrumb context tracking.The
_extract_ast_chunks_with_contextmethod properly tracks parent class/module context as breadcrumbs, solving the "context loss" problem for nested structures. This is valuable for RAG retrieval.
| @Query(value = "SELECT bi FROM BranchIssue bi " + | ||
| "JOIN FETCH bi.codeAnalysisIssue cai " + | ||
| "WHERE bi.branch.id = :branchId AND bi.resolved = false " + | ||
| "ORDER BY cai.id DESC", | ||
| countQuery = "SELECT COUNT(bi) FROM BranchIssue bi WHERE bi.branch.id = :branchId AND bi.resolved = false") | ||
| Page<BranchIssue> findUnresolvedByBranchIdPaged( | ||
| @Param("branchId") Long branchId, | ||
| Pageable pageable | ||
| ); | ||
|
|
||
| @Query(value = "SELECT bi FROM BranchIssue bi " + | ||
| "JOIN FETCH bi.codeAnalysisIssue cai " + | ||
| "WHERE bi.branch.id = :branchId AND bi.resolved = true " + | ||
| "ORDER BY cai.id DESC", | ||
| countQuery = "SELECT COUNT(bi) FROM BranchIssue bi WHERE bi.branch.id = :branchId AND bi.resolved = true") | ||
| Page<BranchIssue> findResolvedByBranchIdPaged( | ||
| @Param("branchId") Long branchId, | ||
| Pageable pageable | ||
| ); | ||
|
|
||
| @Query(value = "SELECT bi FROM BranchIssue bi " + | ||
| "JOIN FETCH bi.codeAnalysisIssue cai " + | ||
| "WHERE bi.branch.id = :branchId " + | ||
| "ORDER BY cai.id DESC", | ||
| countQuery = "SELECT COUNT(bi) FROM BranchIssue bi WHERE bi.branch.id = :branchId") | ||
| Page<BranchIssue> findAllByBranchIdPaged( | ||
| @Param("branchId") Long branchId, | ||
| Pageable pageable | ||
| ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid JOIN FETCH with pagination to prevent in-memory pagination.
All three paged query methods use JOIN FETCH with Pageable, which is a known JPA anti-pattern. When combining JOIN FETCH with pagination, Hibernate cannot apply LIMIT/OFFSET at the database level and instead fetches all matching rows into memory, then paginates in-memory. This causes performance degradation and may trigger "HHH000104" warnings.
Consider removing the JOIN FETCH clauses from these paged queries. If eager loading of codeAnalysisIssue is required, use entity graphs (@EntityGraph) or handle the association loading in the service layer.
🔎 Proposed refactor to remove JOIN FETCH from paged queries
- @Query(value = "SELECT bi FROM BranchIssue bi " +
- "JOIN FETCH bi.codeAnalysisIssue cai " +
- "WHERE bi.branch.id = :branchId AND bi.resolved = false " +
- "ORDER BY cai.id DESC",
+ @Query(value = "SELECT bi FROM BranchIssue bi " +
+ "JOIN bi.codeAnalysisIssue cai " +
+ "WHERE bi.branch.id = :branchId AND bi.resolved = false " +
+ "ORDER BY cai.id DESC",
countQuery = "SELECT COUNT(bi) FROM BranchIssue bi WHERE bi.branch.id = :branchId AND bi.resolved = false")
Page<BranchIssue> findUnresolvedByBranchIdPaged(
@Param("branchId") Long branchId,
Pageable pageable
);
- @Query(value = "SELECT bi FROM BranchIssue bi " +
- "JOIN FETCH bi.codeAnalysisIssue cai " +
- "WHERE bi.branch.id = :branchId AND bi.resolved = true " +
- "ORDER BY cai.id DESC",
+ @Query(value = "SELECT bi FROM BranchIssue bi " +
+ "JOIN bi.codeAnalysisIssue cai " +
+ "WHERE bi.branch.id = :branchId AND bi.resolved = true " +
+ "ORDER BY cai.id DESC",
countQuery = "SELECT COUNT(bi) FROM BranchIssue bi WHERE bi.branch.id = :branchId AND bi.resolved = true")
Page<BranchIssue> findResolvedByBranchIdPaged(
@Param("branchId") Long branchId,
Pageable pageable
);
- @Query(value = "SELECT bi FROM BranchIssue bi " +
- "JOIN FETCH bi.codeAnalysisIssue cai " +
- "WHERE bi.branch.id = :branchId " +
- "ORDER BY cai.id DESC",
+ @Query(value = "SELECT bi FROM BranchIssue bi " +
+ "JOIN bi.codeAnalysisIssue cai " +
+ "WHERE bi.branch.id = :branchId " +
+ "ORDER BY cai.id DESC",
countQuery = "SELECT COUNT(bi) FROM BranchIssue bi WHERE bi.branch.id = :branchId")
Page<BranchIssue> findAllByBranchIdPaged(
@Param("branchId") Long branchId,
Pageable pageable
);If eager loading is still required, consider using @EntityGraph as an alternative approach.
🤖 Prompt for AI Agents
In
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/branch/BranchIssueRepository.java
around lines 64-92, the three paged repository methods use JOIN FETCH which
causes Hibernate to perform in-memory pagination; remove the JOIN FETCH clauses
from the query strings for findUnresolvedByBranchIdPaged,
findResolvedByBranchIdPaged and findAllByBranchIdPaged so the database can apply
LIMIT/OFFSET, and if eager loading of codeAnalysisIssue is still required switch
to using @EntityGraph on the repository methods or explicitly fetch the
association in the service layer after retrieving the Page result.
| OkHttpClient httpClient = new OkHttpClient(); | ||
| Request request = new Request.Builder() | ||
| .url(BITBUCKET_TOKEN_URL) | ||
| .addHeader("Authorization", "JWT " + jwt) | ||
| .addHeader("Content-Type", "application/x-www-form-urlencoded") | ||
| .post(RequestBody.create("grant_type=urn:bitbucket:oauth2:jwt", FORM_MEDIA_TYPE)) | ||
| .build(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resource leak: OkHttpClient instance created but never closed.
Each call to refreshBitbucketConnectAppConnection creates a new OkHttpClient with its own thread pool and connection pool. While OkHttp manages idle connections, creating a new instance per request is inefficient and can lead to resource exhaustion under load.
Consider reusing the existing OkHttpClient infrastructure via httpClientFactory or a shared instance.
🔎 Proposed fix
- // Exchange JWT for access token using JWT Bearer grant
- OkHttpClient httpClient = new OkHttpClient();
- Request request = new Request.Builder()
+ // Exchange JWT for access token using JWT Bearer grant
+ OkHttpClient httpClient = new OkHttpClient.Builder()
+ .callTimeout(30, java.util.concurrent.TimeUnit.SECONDS)
+ .build();
+ Request request = new Request.Builder()Alternatively, inject a shared OkHttpClient instance at the class level for token refresh operations, or reuse httpClientFactory to create a minimal unauthenticated client.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java
around lines 327 to 333, a new OkHttpClient() is created per call which leaks
resources and is inefficient; replace the per-call instantiation with a reused
client by either using the existing httpClientFactory to obtain a client or by
injecting/storing a single shared OkHttpClient at the class level for token
refresh operations, update constructors/fields to accept the shared client or
factory if needed, and remove the new OkHttpClient() usage so requests reuse the
shared instance (no per-call creation or manual close required).
| boolean isPrMergeEvent = "pullrequest:fulfilled".equals(eventType) || | ||
| "pull_request.closed".equals(eventType) && payload.rawPayload() != null && | ||
| payload.rawPayload().path("pull_request").path("merged").asBoolean(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for webhook event type handling and GitHub event mappings
echo "=== Searching for GitHub event type handling ==="
rg -nP --type=java 'pull_request.*closed|X-GitHub-Event|eventType.*=|"pull_request"' -g '*Webhook*.java' -g '*webhook*.java' | head -50
echo -e "\n=== Searching for event type constants and mappings ==="
rg -nP --type=java '"pull_request' -g '*Webhook*.java' -g '*webhook*.java' | head -30
echo -e "\n=== Checking ProviderWebhookController for event type sources ==="
rg -nP --type=java -B5 'eventType' java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.java | head -60Repository: rostilos/CodeCrow
Length of output: 11099
🌐 Web query:
GitHub webhook pull_request event - what is the correct event type header value and format for closed pull requests? Is it "pull_request" with action="closed" or "pull_request.closed"?
💡 Result:
Use X-GitHub-Event: "pull_request" and check the payload's action field (action: "closed"). GitHub does not send an event header like "pull_request.closed". (docs.github.com)
Citations:
Fix GitHub event type check for merged PR detection.
The condition checking for merged GitHub PRs is unreachable. GitHub webhooks send X-GitHub-Event: "pull_request" (not "pull_request.closed"), with the action specified in the payload's action field. The check at line 307:
"pull_request.closed".equals(eventType)will never match GitHub events. Merged GitHub PRs will not be detected and will incorrectly create PR analysis jobs instead of branch analysis jobs.
Change to check eventType.equals("pull_request") and verify the action field in the payload is "closed" and the merged flag is true.
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.java
around lines 306 to 308, the GitHub merged-PR check currently compares eventType
to "pull_request.closed" which never occurs; change the logic to detect GitHub
pull request events by checking eventType.equals("pull_request") (or
"pull_request" equals eventType) and then inspect the payload: ensure
payload.rawPayload() is not null, payload.rawPayload().path("action").asText("")
equals "closed", and
payload.rawPayload().path("pull_request").path("merged").asBoolean(false) is
true; keep existing Bitbucket/other checks intact and add null-safe handling to
avoid NPEs.
...src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java
Show resolved
Hide resolved
| @RequestParam(required = false, defaultValue = "1") int page, | ||
| @RequestParam(required = false, defaultValue = "50") int pageSize |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing input validation for pagination parameters.
Negative or zero values for page will cause PageRequest.of(page - 1, pageSize) to throw IllegalArgumentException. Consider adding validation or clamping values.
🔎 Proposed validation
public ResponseEntity<Map<String, Object>> listBranchIssues(
@PathVariable String workspaceSlug,
@PathVariable String projectNamespace,
@PathVariable String branchName,
@RequestParam(required = false, defaultValue = "open") String status,
@RequestParam(required = false, defaultValue = "1") int page,
@RequestParam(required = false, defaultValue = "50") int pageSize
) {
+ // Clamp pagination values to safe bounds
+ page = Math.max(1, page);
+ pageSize = Math.clamp(pageSize, 1, 100);
+
Workspace workspace = workspaceService.getWorkspaceBySlug(workspaceSlug);🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java
around lines 101-102, the pagination parameters lack validation which can cause
PageRequest.of(page - 1, pageSize) to throw for non-positive values; add input
validation to ensure page is at least 1 and pageSize is positive (optionally
clamp pageSize to a sensible max like 100), then use the validated/clamped
values when calling PageRequest.of(page - 1, pageSize); if you prefer strict
validation, return a 400 Bad Request with a clear message when inputs are
invalid instead of clamping.
| # Tree-sitter for AST-based code parsing (new API) | ||
| tree-sitter>=0.23.0 | ||
| tree-sitter-python>=0.23.0 | ||
| tree-sitter-java>=0.23.0 | ||
| tree-sitter-javascript>=0.23.0 | ||
| tree-sitter-typescript>=0.23.0 | ||
| tree-sitter-go>=0.23.0 | ||
| tree-sitter-rust>=0.23.0 | ||
| tree-sitter-c>=0.23.0 | ||
| tree-sitter-cpp>=0.23.0 | ||
| tree-sitter-c-sharp>=0.23.0 | ||
| tree-sitter-ruby>=0.23.0 | ||
| tree-sitter-php>=0.23.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
latest tree-sitter python package version 2024 2025 breaking changes
💡 Result:
Short answer — latest (checked Dec 31, 2025) and breaking changes:
-
tree-sitter-python (the Python grammar package): latest release shown on PyPI is 0.25.0 (released Sep 11, 2025). In 2024 the notable releases were 0.23.0 (Aug 26, 2024) and a series of 0.23.x updates in late 2024. [1]
-
py-tree-sitter (the official Python bindings): see GitHub releases — v0.24.0 (latest release tag on the releases page) documents API additions and lists deprecations; the release notes mark breaking changes with “!” (review that changelog for any APIs you use). Earlier 2024 tags include v0.21.0 / v0.23.x. [2]
-
tree-sitter (the core Python package/wheels): PyPI shows more recent core binding packages (example: tree-sitter 0.25.x series on PyPI). These releases can include ABI/wheel changes that may affect compatibility. [3]
If you want, I can:
- fetch and paste the specific breaking-change bullets from the py-tree-sitter v0.24.0 changelog, or
- check a specific version (e.g., v0.23.0 → v0.24.0) for exact breaking API changes that could affect your code.
Sources:
[1] PyPI — tree-sitter-python release history.
[2] GitHub — tree-sitter/py-tree-sitter releases (v0.24.0 changelog).
[3] PyPI — tree-sitter package page.
🌐 Web query:
tree-sitter 0.23.0 compatibility newer versions 0.24 0.25
💡 Result:
Short answer: Not fully compatible — there were API/ABI changes after 0.23 so moving to 0.24/0.25 can break binaries and language bindings; dependent native programs or extensions typically must be rebuilt or updated to matching bindings. [1][2][3]
Details / evidence (sources)
- py-tree-sitter v0.24+ release notes mark breaking changes to the bindings API. [1]
- tree-sitter core releases (0.24/0.25) include fixes/features across the C API and build, and multiple releases after 0.23 — these are not purely bugfixes and have caused downstream rebuild needs. [2]
- Reports from downstream users (e.g., Emacs packaging users) show that upgrading libtree-sitter from 0.23 → 0.24+ can be binary-incompatible and required rebuilding Emacs or downgrading. [3]
Recommendation: If you maintain a program that links libtree-sitter (native extension, editor, or packaged app), rebuild it against the tree-sitter version you install (or keep the older libtree-sitter ABI installed alongside if your distro provides parallel sonames). If you want, tell me which binding/app (Emacs, py-tree-sitter, node, etc.) and I’ll give concrete rebuild/upgrade steps.
Sources
[1] py-tree-sitter releases (v0.24.0, v0.25.0 changelogs).
[2] tree-sitter GitHub releases (v0.24/v0.25 release notes).
[3] Downstream reports (packaging/Reddit threads about Emacs + libtree-sitter upgrade).
Review tree-sitter version constraints and test compatibility with 0.24+ releases.
The tree-sitter packages pinned to >=0.23.0 allow dependency resolution to upgrade to 0.24.0 and 0.25.0, which introduce breaking API and ABI changes. The claim that these are "backward-compatible updates" is incorrect—py-tree-sitter v0.24.0+ documents breaking changes in its release notes, and downstream users report binary incompatibility when upgrading from 0.23 to 0.24+. Either pin more conservatively (e.g., >=0.23.0,<0.24.0) or verify that the codebase is fully compatible with the newer versions.
🤖 Prompt for AI Agents
In python-ecosystem/rag-pipeline/requirements.txt around lines 23 to 35 the
tree-sitter packages are allowed to upgrade to 0.24+ which contain breaking
API/ABI changes; update the constraints to prevent accidental upgrades by
pinning them to the 0.23.x series (for example change each
tree-sitter...>=0.23.0 to tree-sitter...>=0.23.0,<0.24.0) or alternatively run a
compatibility test suite and explicitly verify and document support for 0.24+
before loosening the bounds.
| workspace: str | ||
| project: str | ||
| branch: str | ||
| branch: Optional[str] = None # Optional - if None, return empty context |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# First, let's examine the file structure and the specific lines mentioned
wc -l python-ecosystem/rag-pipeline/src/rag_pipeline/api/api.pyRepository: rostilos/CodeCrow
Length of output: 122
🏁 Script executed:
# Read around line 58 to see the branch field definition
sed -n '50,70p' python-ecosystem/rag-pipeline/src/rag_pipeline/api/api.pyRepository: rostilos/CodeCrow
Length of output: 744
🏁 Script executed:
# Read the get_pr_context function and the early-return guard (lines 254-285)
sed -n '254,290p' python-ecosystem/rag-pipeline/src/rag_pipeline/api/api.pyRepository: rostilos/CodeCrow
Length of output: 1503
🏁 Script executed:
# Search for all callers of the /query/pr-context endpoint
rg -n 'pr-context|get_pr_context' python-ecosystem/rag-pipeline/src/Repository: rostilos/CodeCrow
Length of output: 262
🏁 Script executed:
# Search for the query_service.get_context_for_pr method to check its signature
rg -n 'def get_context_for_pr' python-ecosystem/rag-pipeline/Repository: rostilos/CodeCrow
Length of output: 165
🏁 Script executed:
# Check how get_context_for_pr is called and what type hints it has
rg -n -A5 'def get_context_for_pr' python-ecosystem/rag-pipeline/Repository: rostilos/CodeCrow
Length of output: 685
🏁 Script executed:
# Search for callers of get_pr_context endpoint (both API consumers and code references)
rg -n 'get_pr_context|/query/pr-context' python-ecosystem/rag-pipeline/ --type=pyRepository: rostilos/CodeCrow
Length of output: 262
🏁 Script executed:
# Check if there are any test files that call this endpoint
fd -e '\.py$' -path '*/test*' python-ecosystem/rag-pipeline/ | head -20Repository: rostilos/CodeCrow
Length of output: 229
🏁 Script executed:
# Look for any client code that makes requests to pr-context endpoint
rg -n 'pr-context' python-ecosystem/ --type=py --type=ts --type=jsRepository: rostilos/CodeCrow
Length of output: 264
🏁 Script executed:
# Read the complete get_context_for_pr method signature to understand full parameter handling
sed -n '153,175p' python-ecosystem/rag-pipeline/src/rag_pipeline/services/query_service.pyRepository: rostilos/CodeCrow
Length of output: 991
🏁 Script executed:
# Check how the rag_client calls the endpoint with the branch parameter
sed -n '95,115p' python-ecosystem/mcp-client/service/rag_client.pyRepository: rostilos/CodeCrow
Length of output: 1016
🏁 Script executed:
# Search for where PRContextRequest is constructed to see if callers prepare for optional branch
rg -n -B3 -A3 'PRContextRequest' python-ecosystem/Repository: rostilos/CodeCrow
Length of output: 1275
🏁 Script executed:
# Check the full method signature of get_context_for_pr including the type hints for branch parameter
sed -n '153,160p' python-ecosystem/rag-pipeline/src/rag_pipeline/services/query_service.pyRepository: rostilos/CodeCrow
Length of output: 322
🏁 Script executed:
# Check where branch parameter is set in the rag_client payload construction
sed -n '80,110p' python-ecosystem/mcp-client/service/rag_client.pyRepository: rostilos/CodeCrow
Length of output: 1369
🏁 Script executed:
# Look for any tests that exercise the pr-context endpoint
fd -type f -name '*.py' python-ecosystem/ | xargs grep -l 'pr-context\|get_pr_context' | head -10Repository: rostilos/CodeCrow
Length of output: 231
🏁 Script executed:
# Check if there are any other callers of get_context_for_pr within the codebase
rg -n 'get_context_for_pr' python-ecosystem/Repository: rostilos/CodeCrow
Length of output: 617
🏁 Script executed:
# Check how webhook_integration calls get_context_for_pr
sed -n '170,185p' python-ecosystem/rag-pipeline/src/rag_pipeline/services/webhook_integration.pyRepository: rostilos/CodeCrow
Length of output: 529
🏁 Script executed:
# Check the examples.py to see if it provides branch
sed -n '80,95p' python-ecosystem/rag-pipeline/scripts/examples.pyRepository: rostilos/CodeCrow
Length of output: 519
🏁 Script executed:
# Check the full signature of get_pr_context function including where it passes branch to downstream
sed -n '254,295p' python-ecosystem/rag-pipeline/src/rag_pipeline/api/api.pyRepository: rostilos/CodeCrow
Length of output: 1655
API contract change requires type-safety review.
Making branch optional at the endpoint level introduces a type inconsistency: PRContextRequest declares branch: Optional[str], but the downstream query_service.get_context_for_pr() method at line 157 still requires branch: str (non-optional).
The early-return guard (lines 265-279) prevents None from reaching the downstream method in practice, but this masks a type safety violation. Either:
- Update
get_context_for_pr()to acceptbranch: Optional[str], or - Add an explicit type assertion/cast when passing the branch
Additionally, update the docstring (lines 255-263) to document the behavior when branch is None:
Get context for PR review with Lost-in-the-Middle protection.
If branch is not provided (None), returns empty context immediately
without performing RAG queries.
Implements:
- Query decomposition for comprehensive coverage
- Priority-based reranking (core files boosted)
- Relevance threshold filtering
- Deduplication
🤖 Prompt for AI Agents
In python-ecosystem/rag-pipeline/src/rag_pipeline/api/api.py around line 58 (and
note downstream call at ~157 and docstring/guard region around 255-279), the
endpoint makes PRContextRequest.branch Optional[str] but passes it to
query_service.get_context_for_pr which expects a non-optional str; fix by either
changing get_context_for_pr signature to accept branch: Optional[str] and handle
None there, or keep get_context_for_pr as-is and add an explicit runtime
assertion/cast (e.g., if branch is None raise/return earlier or cast with a
clear comment) immediately before the call so types align; also update the
docstring at lines 255-263 to state that when branch is None the endpoint
returns empty context immediately (include the provided bullet points for
behavior).
| for i, chunk in enumerate(chunks): | ||
| if not chunk or not chunk.strip(): | ||
| continue | ||
|
|
||
| if len(chunk.strip()) < self.min_chunk_size and len(chunks) > 1: | ||
| continue | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same small-chunk skipping issue as SemanticCodeSplitter.
Small chunks are skipped without being merged with adjacent chunks, potentially losing content. This mirrors the same pattern flagged in semantic_splitter.py.
🔎 Suggested fix to accumulate small chunks
+ pending_small = ""
for i, chunk in enumerate(chunks):
if not chunk or not chunk.strip():
continue
if len(chunk.strip()) < self.min_chunk_size and len(chunks) > 1:
+ pending_small += chunk + "\n"
continue
+
+ if pending_small:
+ chunk = pending_small + chunk
+ pending_small = ""🤖 Prompt for AI Agents
In python-ecosystem/rag-pipeline/src/rag_pipeline/core/ast_splitter.py around
lines 950-956, small chunks are currently skipped when their stripped length is
below self.min_chunk_size which can drop content; instead modify the loop to
accumulate/merge such small chunks into an adjacent chunk: when encountering a
small chunk, if there is a previous output chunk append the small chunk to it
(with a separator as needed), otherwise buffer it and prepend or merge it into
the next non-empty chunk; ensure you still skip empty/whitespace-only chunks,
preserve original order, update any length checks for the merged chunk, and
avoid duplicating content by not yielding the small chunk separately.
| def _resolve_alias_to_collection(self, alias_name: str) -> Optional[str]: | ||
| """Resolve an alias to its underlying collection name""" | ||
| try: | ||
| aliases = self.qdrant_client.get_collection_aliases(alias_name) | ||
| for alias in aliases.aliases: | ||
| if alias.alias_name == alias_name: | ||
| return alias.collection_name | ||
| except Exception: | ||
| pass | ||
| return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect API usage in _resolve_alias_to_collection.
The method get_collection_aliases(alias_name) expects a collection name, not an alias name. It returns aliases that point TO that collection. This logic is inverted - you're passing an alias name and expecting to find itself in the results, which won't work correctly.
🔎 Suggested fix
def _resolve_alias_to_collection(self, alias_name: str) -> Optional[str]:
"""Resolve an alias to its underlying collection name"""
try:
- aliases = self.qdrant_client.get_collection_aliases(alias_name)
- for alias in aliases.aliases:
- if alias.alias_name == alias_name:
- return alias.collection_name
+ # get_aliases() returns all aliases in the system
+ aliases = self.qdrant_client.get_aliases()
+ for alias in aliases.aliases:
+ if alias.alias_name == alias_name:
+ return alias.collection_name
except Exception:
pass
return None🧰 Tools
🪛 Ruff (0.14.10)
152-153: try-except-pass detected, consider logging the exception
(S110)
152-152: Do not catch blind exception: Exception
(BLE001)
🤖 Prompt for AI Agents
In python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.py around
lines 145-154, the code calls get_collection_aliases(alias_name) using an alias
but the Qdrant API expects a collection name and returns aliases for that
collection; to fix this, iterate over all collections (e.g., from
self.qdrant_client.get_collections() or list_collections), call
get_collection_aliases(collection_name) for each collection, and if any returned
alias.alias_name equals the provided alias_name return that collection's name;
handle exceptions per-call and return None if no match is found.
| for node, embedding in zip(batch, embeddings): | ||
| node.embedding = embedding |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add strict=True to zip() to catch length mismatches.
If embeddings has fewer elements than batch (e.g., due to an API error returning partial results), data would silently be lost. Using strict=True will raise a ValueError if lengths don't match.
🔎 Suggested fix
# Set embeddings on nodes
- for node, embedding in zip(batch, embeddings):
+ for node, embedding in zip(batch, embeddings, strict=True):
node.embedding = embedding📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for node, embedding in zip(batch, embeddings): | |
| node.embedding = embedding | |
| for node, embedding in zip(batch, embeddings, strict=True): | |
| node.embedding = embedding |
🧰 Tools
🪛 Ruff (0.14.10)
346-346: zip() without an explicit strict= parameter
Add explicit value for parameter strict=
(B905)
🤖 Prompt for AI Agents
In python-ecosystem/rag-pipeline/src/rag_pipeline/core/index_manager.py around
lines 346-347, the loop using zip(batch, embeddings) can silently drop nodes
when embeddings is shorter; change to zip(batch, embeddings, strict=True) to
raise on length mismatch (or, if supporting Python <3.10, explicitly check
len(batch) == len(embeddings) and raise a ValueError before the loop) so partial
API results don't silently corrupt data.
feat: Enhance branch reconciliation by restoring missing diffs from p…
|
/codecrow analyze |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
♻️ Duplicate comments (2)
python-ecosystem/mcp-client/utils/prompt_builder.py (1)
752-759: Unused parameterfull_diff_contentand implicitOptionaltype hints.This was already flagged in a previous review. The
full_diff_contentparameter is declared but never used in the method body (the prompt instructs the agent to retrieve the full diff via MCP tools if needed). Additionally,rag_contextandstructured_contextshould use explicitT | Nonesyntax per PEP 484.🔎 Proposed fix
@staticmethod def build_incremental_review_prompt( pr_metadata: Dict[str, Any], delta_diff_content: str, - full_diff_content: str, - rag_context: Dict[str, Any] = None, - structured_context: Optional[str] = None + rag_context: Optional[Dict[str, Any]] = None, + structured_context: Optional[str] = None ) -> str:python-ecosystem/mcp-client/service/review_service.py (1)
668-682: Remove unusedfull_diff_contentargument from incremental prompt call.This was already flagged in a previous review. The
full_diff_content=formatted_diffargument should be removed once the method signature inprompt_builder.pyis updated.🔎 Proposed fix
prompt = PromptBuilder.build_incremental_review_prompt( pr_metadata=pr_metadata, delta_diff_content=request.deltaDiff, - full_diff_content=formatted_diff, rag_context=rag_context, structured_context=structured_context )
🧹 Nitpick comments (6)
python-ecosystem/mcp-client/utils/prompt_builder.py (1)
844-844: Minor inconsistency between "Report ALL issues" and deduplication guidance.Line 844 instructs to "Report ALL issues found in delta diff. Do not group them or omit them for brevity." This appears to contradict the
ISSUE_DEDUPLICATION_INSTRUCTIONSconstant added earlier, which explicitly instructs merging similar issues. Consider clarifying whether deduplication should apply to incremental reviews.python-ecosystem/mcp-client/service/review_service.py (3)
42-44: Hardcoded path with commented alternative.The commented-out path and the hardcoded
/app/codecrow-vcs-mcp-1.0.jarpath suggest this is deployment-specific configuration. Consider using environment variables exclusively or documenting the expected deployment structure.
247-248: Remove extraneousfprefix from string literal.The f-string on line 248 has no placeholders. Remove the
fprefix.🔎 Proposed fix
error_response = ResponseParser.create_error_response( - f"Agent execution failed", sanitized_message + "Agent execution failed", sanitized_message )
670-694: Remove extraneousfprefixes from log statements.Lines 670, 685, and 694 use f-strings without any placeholders.
🔎 Proposed fix
- logger.info(f"Using INCREMENTAL analysis mode with delta diff") + logger.info("Using INCREMENTAL analysis mode with delta diff") ... - logger.info(f"Using FULL analysis mode with previous analysis data") + logger.info("Using FULL analysis mode with previous analysis data") ... - logger.info(f"Using FULL analysis mode (first review)") + logger.info("Using FULL analysis mode (first review)")python-ecosystem/mcp-client/service/issue_post_processor.py (2)
32-46: Consider annotating mutable class attribute withClassVar.The
ISSUE_KEYWORDSlist is a mutable class attribute. While it's not modified at runtime, annotating it withClassVarmakes the intent explicit and satisfies static analysis.🔎 Proposed fix
+from typing import ClassVar, Dict, Any, List, Optional, Tuple -from typing import Dict, Any, List, Optional, Tuple ... class IssuePostProcessor: ... # Keywords that indicate similar issues (for grouping) - ISSUE_KEYWORDS = [ + ISSUE_KEYWORDS: ClassVar[List[str]] = [ 'hardcode', 'hardcoded', ... ]
536-568:IssueDeduplicatoraccesses private methods ofIssuePostProcessor.The
find_duplicatesmethod callsself.processor._calculate_issue_similarity, which is a private method (prefixed with_). Consider either making_calculate_issue_similaritypublic or moving the deduplication logic to be fully withinIssuePostProcessor.🔎 Proposed approach
Either rename the method to make it public:
- def _calculate_issue_similarity( + def calculate_issue_similarity(Or consolidate the logic within one class to avoid cross-class private method access.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
python-ecosystem/mcp-client/service/issue_post_processor.pypython-ecosystem/mcp-client/service/review_service.pypython-ecosystem/mcp-client/utils/prompt_builder.py
🧰 Additional context used
🪛 Ruff (0.14.10)
python-ecosystem/mcp-client/service/review_service.py
244-244: Use explicit conversion flag
Replace with conversion flag
(RUF010)
248-248: f-string without any placeholders
Remove extraneous f prefix
(F541)
414-414: Use explicit conversion flag
Replace with conversion flag
(RUF010)
670-670: f-string without any placeholders
Remove extraneous f prefix
(F541)
685-685: f-string without any placeholders
Remove extraneous f prefix
(F541)
694-694: f-string without any placeholders
Remove extraneous f prefix
(F541)
python-ecosystem/mcp-client/utils/prompt_builder.py
756-756: Unused static method argument: full_diff_content
(ARG004)
757-757: PEP 484 prohibits implicit Optional
Convert to T | None
(RUF013)
python-ecosystem/mcp-client/service/issue_post_processor.py
32-46: Mutable class attributes should be annotated with typing.ClassVar
(RUF012)
🔇 Additional comments (8)
python-ecosystem/mcp-client/utils/prompt_builder.py (1)
20-78: Well-structured line number and deduplication instructions.The new constants
LINE_NUMBER_INSTRUCTIONSandISSUE_DEDUPLICATION_INSTRUCTIONSprovide clear, actionable guidance for the LLM. The example-driven approach for line number calculation and the before/after examples for deduplication will help improve output quality.python-ecosystem/mcp-client/service/review_service.py (2)
203-232: Well-implemented post-processing integration.Good defensive checks for result validity, proper handling of both Pydantic models and plain dicts, and informative logging of issue count changes after deduplication.
763-770: Early return for missing target branch is appropriate.The guard against missing
targetBranchNameprevents RAG queries from failing. The warning log level seems appropriate since this is an unexpected condition for PR reviews.python-ecosystem/mcp-client/service/issue_post_processor.py (5)
102-106: Edge case: empty lines in diff handling.The condition
line == ''at line 102 will match truly empty lines (no leading space), but in unified diff format, context lines should always have a leading space. This may cause issues with malformed diffs that have stripped trailing whitespace.
338-360: Consider marking merged issues to prevent re-comparison.In the nested loop, when
issue1is found to be similar to multiple issues, those get added tosimilar_group. However,issue1itself is not added tomerged_indices, so it will be included inresulteven if it was the first element of a merged group. The logic appears correct sincesimilar_groupstarts withissue1, but the intent could be clearer.
364-411: Solid multi-signal similarity calculation.The weighted combination of keyword overlap (40%), sequence similarity (30%), line proximity (20%), and category match (10%) provides a balanced approach to detecting semantic duplicates. The 50-line decay for proximity is reasonable.
571-632: Robust diff restoration logic.The function properly handles both string and integer IDs, only restores diffs for unresolved issues, and checks for various empty/placeholder diff values. The logging of restoration count aids debugging.
635-671: Clean convenience wrapper with helpful metadata.The function provides a single entry point for all post-processing steps. The metadata flags (
_post_processed,_original_issue_count,_final_issue_count) are useful for debugging and observability.
|
/codecrow analyze |
|
Comment commands are not enabled for this project Check the job logs in CodeCrow for detailed error information. |
|
| Severity | Count | |
|---|---|---|
| 🔴 High | 3 | Critical issues requiring immediate attention |
| 🟡 Medium | 4 | Issues that should be addressed |
Detailed Issues
🔴 High Severity Issues
Id on Platform: 677
Category: 🔒 Security
File: .../vcsclient/VcsClientProvider.java:320
Issue: The JWT Bearer grant implementation for Bitbucket Connect Apps uses a hardcoded 3-minute expiration and lacks audience binding, increasing replay attack risks.
💡 Suggested Fix
Add the 'aud' (audience) claim to the JWT, pointing to the Bitbucket token endpoint, and ideally make the expiration configurable.
--- a/java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java
+++ b/java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java
@@ -320,1 +320,2 @@
- .setExpiration(new Date(System.currentTimeMillis() + 180000))
+ .setAudience("https://bitbucket.org/site/oauth2/access_token")
+ .setExpiration(new Date(System.currentTimeMillis() + 180000))Id on Platform: 680
Category: 🏗️ Architecture
File: .../service/JobService.java:318
Issue: Previous issue 625 persists. Potential RowValueOutOfDate exceptions due to improper transactional ordering.
💡 Suggested Fix
Implement explicit locking or refine the transactional boundary to ensure atomic state transitions.
--- a/java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/JobService.java
+++ b/java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/JobService.java
@@ -318,1 +318,1 @@
- @Transactional
+ @Transactional(isolation = Isolation.REPEATABLE_READ)Id on Platform: 683
Category: 🐛 Bug Risk
File: .../llm/llm_factory.py:196
Issue: Previous issue 629 persists. Temperature for Gemini 3 models is still 0.1 instead of 0, which may lead to non-deterministic analysis results.
💡 Suggested Fix
Set the default temperature for Gemini models to 0 for deterministic code analysis.
--- a/python-ecosystem/mcp-client/llm/llm_factory.py
+++ b/python-ecosystem/mcp-client/llm/llm_factory.py
@@ -196,1 +196,1 @@
- temperature=0.1,
+ temperature=0,🟡 Medium Severity Issues
Id on Platform: 678
Category: ✨ Best Practices
File: .../vcsclient/VcsClientProvider.java:327
Issue: Creating a new OkHttpClient instance for every token refresh prevents connection pooling and leads to resource exhaustion.
💡 Suggested Fix
Use a shared, static, or bean-managed OkHttpClient instance.
--- a/java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java
+++ b/java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java
@@ -327,1 +327,1 @@
- OkHttpClient client = new OkHttpClient();
+ OkHttpClient client = this.sharedHttpClient;Id on Platform: 679
Category: 🐛 Bug Risk
File: .../controller/ProviderWebhookController.java:306
Issue: The logic assumes the target branch is always available in the payload. Missing null checks for 'targetBranch()' could lead to NPE or incorrect job targeting.
💡 Suggested Fix
Add null safety checks and fallback logic when extracting the target branch from the webhook payload.
--- a/java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.java
+++ b/java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.java
@@ -306,1 +306,5 @@
- String branch = payload.targetBranch();
+ String branch = payload.targetBranch();
+ if (branch == null) {
+ logger.error("Target branch missing in webhook payload");
+ return ResponseEntity.badRequest().build();
+ }Id on Platform: 681
Category: 🛡️ Error Handling
File: .../service/WebhookAsyncProcessor.java:443
Issue: Previous issue 626 persists. Error sanitization remains fragile.
💡 Suggested Fix
Add null checks and a robust default message to the sanitization utility.
--- a/java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookAsyncProcessor.java
+++ b/java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/service/WebhookAsyncProcessor.java
@@ -443,1 +443,1 @@
- return error.getMessage().substring(0, 100);
+ return error != null && error.getMessage() != null ? error.getMessage().substring(0, Math.min(error.getMessage().length(), 100)) : "Unknown error";Id on Platform: 682
Category: 🔒 Security
File: .../controller/AllowedCommandUserController.java:127
Issue: Previous issue 628 persists. The PATCH endpoint lacks secondary ownership verification.
💡 Suggested Fix
Verify that the authenticated user has administrative rights over the project before allowing modification of allowed command users.
--- a/java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/AllowedCommandUserController.java
+++ b/java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/project/controller/AllowedCommandUserController.java
@@ -127,0 +127,1 @@
+ checkProjectOwnership(projectId);Files Affected
- .../vcsclient/VcsClientProvider.java: 2 issues
- .../llm/llm_factory.py: 1 issue
- .../controller/ProviderWebhookController.java: 1 issue
- .../service/WebhookAsyncProcessor.java: 1 issue
- .../service/JobService.java: 1 issue
- .../controller/AllowedCommandUserController.java: 1 issue
Analysis completed on 2025-12-31 11:52:32 | View Full Report | Pull Request
… compiled Python files
…apabilities in branch issues
Enhanced Filtering and Query Capabilities, Issues API improvements
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/CodeAnalysisService.java (1)
22-24: Consolidate redundant repository fields.The class declares two separate fields (
analysisRepositoryandcodeAnalysisRepository) of the same typeCodeAnalysisRepository, which are used interchangeably throughout the code. This creates confusion, increases maintenance burden, and violates the principle of having a single source of truth.🔎 Proposed consolidation
Remove the redundant field and use a single repository instance:
- private final CodeAnalysisRepository analysisRepository; private final CodeAnalysisIssueRepository issueRepository; - private final CodeAnalysisRepository codeAnalysisRepository; + private final CodeAnalysisRepository codeAnalysisRepository; private static final Logger log = LoggerFactory.getLogger(CodeAnalysisService.class); @Autowired public CodeAnalysisService( - CodeAnalysisRepository analysisRepository, CodeAnalysisIssueRepository issueRepository, CodeAnalysisRepository codeAnalysisRepository ) { - this.analysisRepository = analysisRepository; this.issueRepository = issueRepository; this.codeAnalysisRepository = codeAnalysisRepository; }Then update all usages of
analysisRepositoryto usecodeAnalysisRepositoryinstead throughout the class.
♻️ Duplicate comments (3)
python-ecosystem/mcp-client/utils/prompt_builder.py (1)
752-879: Remove unused parameterfull_diff_content- previous review comment not addressed.The
full_diff_contentparameter at line 756 is defined but never used in the method body. The prompt explicitly instructs the agent to "retrieve it via MCP tools ONLY if necessary" (line 832), confirming the full diff should not be embedded.This issue was already flagged in a previous review but remains unresolved.
🔎 Proposed fix
In
prompt_builder.pylines 753-759, remove the unused parameter and fix the type hint:@staticmethod def build_incremental_review_prompt( pr_metadata: Dict[str, Any], delta_diff_content: str, - full_diff_content: str, - rag_context: Dict[str, Any] = None, + rag_context: Optional[Dict[str, Any]] = None, structured_context: Optional[str] = None ) -> str:Also verify and update the call site in
review_service.py(around lines 644-650) to remove the corresponding argument.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java (2)
80-82: N+1 query issue with lazy-loaded issues collection.This issue was already identified in a previous review. The filter condition
!branch.getIssues().isEmpty()on a lazy-loaded collection will trigger a separate database query for each branch, causing an N+1 problem.As previously recommended, consider using only
branch.getTotalIssues() > 0if that field is reliably maintained, or implement a repository method that fetches branches with their issue counts in a single query.
106-107: Missing input validation for pagination parameters.This issue was already identified in a previous review. Negative or zero values for
pagewill causePageRequest.of(page - 1, pageSize)to throw an exception when building pagination offsets on line 204.Add validation to ensure
pageis at least 1 andpageSizeis positive and reasonably bounded (e.g., max 100).🔎 Proposed validation
public ResponseEntity<Map<String, Object>> listBranchIssues( @PathVariable String workspaceSlug, @PathVariable String projectNamespace, @PathVariable String branchName, @RequestParam(required = false, defaultValue = "open") String status, @RequestParam(required = false) String severity, @RequestParam(required = false) String category, @RequestParam(required = false) String filePath, @RequestParam(required = false) String dateFrom, @RequestParam(required = false) String dateTo, @RequestParam(required = false, defaultValue = "1") int page, @RequestParam(required = false, defaultValue = "50") int pageSize ) { + // Validate pagination parameters + page = Math.max(1, page); + pageSize = Math.clamp(pageSize, 1, 100); + Workspace workspace = workspaceService.getWorkspaceBySlug(workspaceSlug);
🧹 Nitpick comments (3)
python-ecosystem/mcp-client/utils/prompt_builder.py (1)
139-140: Consider using explicitOptionaltype hints for parameters withNonedefaults.Multiple methods use implicit
Optional(e.g.,rag_context: Dict[str, Any] = None) which PEP 484 discourages. Consider usingOptional[Dict[str, Any]]orDict[str, Any] | Nonefor clarity.Based on static analysis hints (RUF013).
🔎 Example fix pattern
def build_first_review_prompt( pr_metadata: Dict[str, Any], - rag_context: Dict[str, Any] = None, + rag_context: Optional[Dict[str, Any]] = None, structured_context: Optional[str] = None ) -> str:Apply this pattern to similar parameters in lines 139, 246, 522, 578, and 670.
Also applies to: 246-247, 522-525, 578-580, 670-672
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/analysis/issue/IssueDTO.java (1)
8-29: Remove duplicate fields or clarify their distinct purposes with proper deprecation markers.The record contains duplicate fields storing the same data:
typeandissueCategoryboth hold the issue category (both mapped from the samecategoryStrvalue)pullRequestId(String) andprNumber(Long) both represent the PR number in different typescreatedAtanddetectedAtboth set toissue.getCreatedAt()This duplication pattern also appears in
AiRequestPreviousIssueDTO. To maintain API clarity:
- If these fields represent an API migration, add
@Deprecatedannotations to the old fields and document the migration path- If both sets serve distinct purposes, update the Javadoc to explain the difference
- Consider removing unused fields—
detectedAtis currently defined but unused throughout the codebaseAlternatively, consolidate to a single set of fields that cover all use cases.
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java (1)
222-244: Unused inner class should be removed.The
UpdatePullRequestStatusRequestclass is defined but never used in any of the controller's endpoints. This appears to be dead code, possibly left over from a planned feature that was not implemented.Consider removing this class to reduce code clutter, or if a status update endpoint is planned, add a TODO comment to clarify the intention.
🔎 Proposed removal
return ResponseEntity.ok(response); } - - public static class UpdatePullRequestStatusRequest { - private String status; // approved|changes_requested|merged|closed - private String comment; - - public UpdatePullRequestStatusRequest() { - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getComment() { - return comment; - } - - public void setComment(String comment) { - this.comment = comment; - } - } }
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (8)
python-ecosystem/mcp-client/__pycache__/main.cpython-313.pycis excluded by!**/*.pycpython-ecosystem/mcp-client/model/__pycache__/models.cpython-313.pycis excluded by!**/*.pycpython-ecosystem/mcp-client/server/__pycache__/stdin_handler.cpython-313.pycis excluded by!**/*.pycpython-ecosystem/mcp-client/server/__pycache__/web_server.cpython-313.pycis excluded by!**/*.pycpython-ecosystem/mcp-client/service/__pycache__/review_service.cpython-313.pycis excluded by!**/*.pycpython-ecosystem/mcp-client/utils/__pycache__/prompt_builder.cpython-313.pycis excluded by!**/*.pycpython-ecosystem/mcp-client/utils/__pycache__/prompt_logger.cpython-313.pycis excluded by!**/*.pycpython-ecosystem/mcp-client/utils/__pycache__/response_parser.cpython-313.pycis excluded by!**/*.pyc
📒 Files selected for processing (10)
frontendjava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/analysis/issue/IssueDTO.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/branch/BranchIssueRepository.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/CodeAnalysisService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/AnalysisIssueController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/AnalysisIssueResponse.javapython-ecosystem/mcp-client/.gitignorepython-ecosystem/mcp-client/utils/prompt_builder.pypython-ecosystem/rag-pipeline/.gitignore
✅ Files skipped from review due to trivial changes (1)
- frontend
🚧 Files skipped from review as they are similar to previous changes (3)
- java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/persistence/repository/branch/BranchIssueRepository.java
- java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/AnalysisIssueController.java
- python-ecosystem/rag-pipeline/.gitignore
🧰 Additional context used
🧬 Code graph analysis (2)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/AnalysisIssueController.java (1)
CrossOrigin(22-159)
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/analysis/issue/IssueDTO.java (1)
python-ecosystem/mcp-client/model/models.py (1)
IssueCategory(7-18)
🪛 Ruff (0.14.10)
python-ecosystem/mcp-client/utils/prompt_builder.py
756-756: Unused static method argument: full_diff_content
(ARG004)
757-757: PEP 484 prohibits implicit Optional
Convert to T | None
(RUF013)
🔇 Additional comments (9)
python-ecosystem/mcp-client/.gitignore (1)
14-17: LGTM! Standard ignore patterns for Python project.The added patterns—
*.jarfor build artifacts,logs/**for runtime logs, and**/__pycache__/for Python bytecode caches—are all appropriate. This is a Python-only project with no JAR files or Maven/Gradle build configs, so the patterns are safe and follow conventions.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/dto/response/AnalysisIssueResponse.java (3)
13-13: LGTM!The new field is properly declared with a clear comment explaining its purpose. The nullable String type is appropriate for an optional analysis summary.
39-44: LGTM!The getter and setter methods follow the standard Java bean pattern and are correctly implemented.
1-1: Package rename successfully completed and verified across the codebase.All imports and references to
AnalysisIssueResponsecorrectly use the new package pathorg.rostilos.codecrow.webserver.analysis.dto.response. No stale imports of the old package remain. The refactoring is complete with no references needing correction.python-ecosystem/mcp-client/utils/prompt_builder.py (3)
20-79: LGTM! Excellent additions to improve issue reporting quality.The new
LINE_NUMBER_INSTRUCTIONSandISSUE_DEDUPLICATION_INSTRUCTIONSconstants provide clear, detailed guidance that will help reduce false positives and duplicate issues in code reviews. The examples are particularly helpful.
118-121: LGTM! Priority rebalancing improves RAG context utilization.The adjustment from 60%→50% for high priority and 5%→15% for RAG context is reasonable and should better leverage the additional codebase context provided by RAG.
185-186: LGTM! Consistent integration of new instructions across prompt templates.The new
LINE_NUMBER_INSTRUCTIONSandISSUE_DEDUPLICATION_INSTRUCTIONSare properly integrated into all relevant prompt methods. The emphasis on preservingsuggestedFixDifffor unresolved issues (lines 416-427, 477-478) is particularly important for maintaining issue continuity.Also applies to: 205-206, 305-306, 328-328, 413-427, 477-478, 629-631, 635-635
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/CodeAnalysisService.java (1)
179-181: The methodsfindAnalysisByProjectAndPrNumberAndVersionandfindByProjectIdAndPrNumberAndPrVersionare not duplicates. Although they have similar signatures, they delegate to different repository objects:
findAnalysisByProjectAndPrNumberAndVersion(line 180) delegates tocodeAnalysisRepositoryfindByProjectIdAndPrNumberAndPrVersion(line 298) delegates toanalysisRepositoryBoth methods are actively used in the codebase and serve different purposes. No removal or refactoring is needed.
Likely an incorrect or invalid review comment.
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/analysis/issue/IssueDTO.java (1)
30-58: Good refactoring with proper null safety.The introduction of the local
analysisvariable (line 35) is a clean improvement that avoids repeated method calls and improves readability. The null safety checks using ternary operators are properly implemented throughout.
| public ResponseEntity<List<PullRequestDTO>> listPullRequests( | ||
| @PathVariable String workspaceSlug, | ||
| @PathVariable String projectNamespace, | ||
| @RequestParam(name = "page", defaultValue = "1") int page, | ||
| @RequestParam(name = "pageSize", defaultValue = "20") int pageSize | ||
| ) { | ||
| Workspace workspace = workspaceService.getWorkspaceBySlug(workspaceSlug); | ||
| Project project = projectService.getProjectByWorkspaceAndNamespace(workspace.getId(), projectNamespace); | ||
| List<PullRequest> pullRequestList = pullRequestRepository.findByProject_Id(project.getId()); | ||
| List<PullRequestDTO> pullRequestDTOs = pullRequestList.stream() | ||
| .map(PullRequestDTO::fromPullRequest) | ||
| .toList(); | ||
|
|
||
| return new ResponseEntity<>(pullRequestDTOs, HttpStatus.OK); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pagination parameters are declared but never used.
The method signature accepts page and pageSize parameters with default values, but the implementation fetches all pull requests from the repository without applying pagination. This creates a misleading API contract and may cause performance issues when dealing with projects that have many pull requests.
Either implement pagination using PageRequest.of(page - 1, pageSize) when querying the repository, or remove the unused parameters from the method signature.
🔎 Proposed fix to implement pagination
@GetMapping
@PreAuthorize("@workspaceSecurity.isWorkspaceMember(#workspaceSlug, authentication)")
public ResponseEntity<List<PullRequestDTO>> listPullRequests(
@PathVariable String workspaceSlug,
@PathVariable String projectNamespace,
@RequestParam(name = "page", defaultValue = "1") int page,
@RequestParam(name = "pageSize", defaultValue = "20") int pageSize
) {
+ // Validate and clamp pagination parameters
+ page = Math.max(1, page);
+ pageSize = Math.clamp(pageSize, 1, 100);
+
Workspace workspace = workspaceService.getWorkspaceBySlug(workspaceSlug);
Project project = projectService.getProjectByWorkspaceAndNamespace(workspace.getId(), projectNamespace);
- List<PullRequest> pullRequestList = pullRequestRepository.findByProject_Id(project.getId());
+
+ org.springframework.data.domain.Pageable pageable = org.springframework.data.domain.PageRequest.of(page - 1, pageSize);
+ org.springframework.data.domain.Page<PullRequest> pullRequestPage = pullRequestRepository.findByProject_Id(project.getId(), pageable);
+
- List<PullRequestDTO> pullRequestDTOs = pullRequestList.stream()
+ List<PullRequestDTO> pullRequestDTOs = pullRequestPage.getContent().stream()
.map(PullRequestDTO::fromPullRequest)
.toList();
return new ResponseEntity<>(pullRequestDTOs, HttpStatus.OK);
}Note: This assumes the repository method supports Pageable. If not, you'll need to add a paginated query method to PullRequestRepository.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java
around lines 48 to 62, the method accepts page and pageSize but currently
ignores them and returns all pull requests; change the repository call to use a
Pageable (create PageRequest.of(Math.max(1, page) - 1, pageSize)), call a
paginated repository method like findByProject_Id(project.getId(), pageable) (or
add that method to PullRequestRepository if missing), map the resulting
Page<PullRequest> to Page<PullRequestDTO> (or map page.getContent() to DTOs and
return those plus paging metadata if needed) and return the paginated result
instead of the full list. Ensure page is 1-based in the API and converted to
0-based for PageRequest.
| if (dateFrom != null && !dateFrom.isBlank()) { | ||
| try { | ||
| parsedDateFrom = java.time.OffsetDateTime.parse(dateFrom); | ||
| } catch (Exception e) { | ||
| try { | ||
| parsedDateFrom = java.time.LocalDate.parse(dateFrom).atStartOfDay().atOffset(java.time.ZoneOffset.UTC); | ||
| } catch (Exception ignored) {} | ||
| } | ||
| } | ||
| if (dateTo != null && !dateTo.isBlank()) { | ||
| try { | ||
| parsedDateTo = java.time.OffsetDateTime.parse(dateTo); | ||
| } catch (Exception e) { | ||
| try { | ||
| parsedDateTo = java.time.LocalDate.parse(dateTo).atTime(23, 59, 59).atOffset(java.time.ZoneOffset.UTC); | ||
| } catch (Exception ignored) {} | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Silent exception handling hides invalid date inputs.
The date parsing logic uses empty catch blocks that silently ignore parsing exceptions. When a user provides an invalid date format, the filter is silently ignored (defaults to null), and the user receives no feedback that their filter was invalid.
Consider either:
- Logging the parsing exception to help with debugging
- Returning a 400 Bad Request with a clear error message for invalid date formats
- Documenting the expected date formats in API documentation
This will improve the user experience and make troubleshooting easier.
🔎 Proposed fix with logging
if (dateFrom != null && !dateFrom.isBlank()) {
try {
parsedDateFrom = java.time.OffsetDateTime.parse(dateFrom);
} catch (Exception e) {
try {
parsedDateFrom = java.time.LocalDate.parse(dateFrom).atStartOfDay().atOffset(java.time.ZoneOffset.UTC);
- } catch (Exception ignored) {}
+ } catch (Exception ex) {
+ // Log invalid date format for debugging
+ org.slf4j.LoggerFactory.getLogger(getClass()).warn("Invalid dateFrom format: {}", dateFrom, ex);
+ }
}
}
if (dateTo != null && !dateTo.isBlank()) {
try {
parsedDateTo = java.time.OffsetDateTime.parse(dateTo);
} catch (Exception e) {
try {
parsedDateTo = java.time.LocalDate.parse(dateTo).atTime(23, 59, 59).atOffset(java.time.ZoneOffset.UTC);
- } catch (Exception ignored) {}
+ } catch (Exception ex) {
+ org.slf4j.LoggerFactory.getLogger(getClass()).warn("Invalid dateTo format: {}", dateTo, ex);
+ }
}
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (dateFrom != null && !dateFrom.isBlank()) { | |
| try { | |
| parsedDateFrom = java.time.OffsetDateTime.parse(dateFrom); | |
| } catch (Exception e) { | |
| try { | |
| parsedDateFrom = java.time.LocalDate.parse(dateFrom).atStartOfDay().atOffset(java.time.ZoneOffset.UTC); | |
| } catch (Exception ignored) {} | |
| } | |
| } | |
| if (dateTo != null && !dateTo.isBlank()) { | |
| try { | |
| parsedDateTo = java.time.OffsetDateTime.parse(dateTo); | |
| } catch (Exception e) { | |
| try { | |
| parsedDateTo = java.time.LocalDate.parse(dateTo).atTime(23, 59, 59).atOffset(java.time.ZoneOffset.UTC); | |
| } catch (Exception ignored) {} | |
| } | |
| } | |
| if (dateFrom != null && !dateFrom.isBlank()) { | |
| try { | |
| parsedDateFrom = java.time.OffsetDateTime.parse(dateFrom); | |
| } catch (Exception e) { | |
| try { | |
| parsedDateFrom = java.time.LocalDate.parse(dateFrom).atStartOfDay().atOffset(java.time.ZoneOffset.UTC); | |
| } catch (Exception ex) { | |
| // Log invalid date format for debugging | |
| org.slf4j.LoggerFactory.getLogger(getClass()).warn("Invalid dateFrom format: {}", dateFrom, ex); | |
| } | |
| } | |
| } | |
| if (dateTo != null && !dateTo.isBlank()) { | |
| try { | |
| parsedDateTo = java.time.OffsetDateTime.parse(dateTo); | |
| } catch (Exception e) { | |
| try { | |
| parsedDateTo = java.time.LocalDate.parse(dateTo).atTime(23, 59, 59).atOffset(java.time.ZoneOffset.UTC); | |
| } catch (Exception ex) { | |
| org.slf4j.LoggerFactory.getLogger(getClass()).warn("Invalid dateTo format: {}", dateTo, ex); | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java
around lines 131-148, the date parsing swallows exceptions in empty catch blocks
causing invalid date inputs to be ignored; change the handlers to validate and
report errors: catch specific parsing exceptions, log the exception and input
(using the controller logger) and return a 400 Bad Request (or throw a mapped
BadRequestException) with a clear message indicating accepted date formats (e.g.
ISO_OFFSET_DATE_TIME or ISO_LOCAL_DATE) instead of silently setting
parsedDateFrom/To to null; ensure both dateFrom and dateTo paths follow the same
behavior and include unit/handler tests or integration test updates as needed.
| //TODO: use SQL instead.... | ||
| // Fetch all issues for the branch and filter in Java (avoids complex SQL with nullable params) | ||
| List<BranchIssue> allBranchIssues = branchIssueRepository.findAllByBranchIdWithIssues(branch.getId()); | ||
|
|
||
| // Apply filters in Java | ||
| final java.time.OffsetDateTime finalDateFrom = parsedDateFrom; | ||
| final java.time.OffsetDateTime finalDateTo = parsedDateTo; | ||
|
|
||
| List<BranchIssue> filteredIssues = allBranchIssues.stream() | ||
| .filter(bi -> { | ||
| // Status filter | ||
| if ("open".equals(normalizedStatus) && bi.isResolved()) return false; | ||
| if ("resolved".equals(normalizedStatus) && !bi.isResolved()) return false; | ||
| // "all" passes everything | ||
|
|
||
| var issue = bi.getCodeAnalysisIssue(); | ||
| if (issue == null) return false; | ||
|
|
||
| // Severity filter | ||
| if (normalizedSeverity != null) { | ||
| if (issue.getSeverity() == null) return false; | ||
| if (!normalizedSeverity.equals(issue.getSeverity().name())) return false; | ||
| } | ||
|
|
||
| // Category filter | ||
| if (normalizedCategory != null) { | ||
| if (issue.getIssueCategory() == null) return false; | ||
| if (!normalizedCategory.equals(issue.getIssueCategory().name())) return false; | ||
| } | ||
|
|
||
| // File path filter (partial match, case insensitive) | ||
| if (normalizedFilePath != null) { | ||
| if (issue.getFilePath() == null) return false; | ||
| if (!issue.getFilePath().toLowerCase().contains(normalizedFilePath)) return false; | ||
| } | ||
|
|
||
| // Date from filter | ||
| if (finalDateFrom != null && issue.getCreatedAt() != null) { | ||
| if (issue.getCreatedAt().isBefore(finalDateFrom)) return false; | ||
| } | ||
|
|
||
| // Date to filter | ||
| if (finalDateTo != null && issue.getCreatedAt() != null) { | ||
| if (issue.getCreatedAt().isAfter(finalDateTo)) return false; | ||
| } | ||
|
|
||
| return true; | ||
| }) | ||
| .sorted((a, b) -> Long.compare(b.getCodeAnalysisIssue().getId(), a.getCodeAnalysisIssue().getId())) | ||
| .toList(); | ||
|
|
||
| long total = filteredIssues.size(); | ||
|
|
||
| // Apply pagination | ||
| int startIndex = (page - 1) * pageSize; | ||
| int endIndex = Math.min(startIndex + pageSize, filteredIssues.size()); | ||
|
|
||
| List<IssueDTO> pagedIssues = (startIndex < filteredIssues.size()) | ||
| ? filteredIssues.subList(startIndex, endIndex).stream() | ||
| .map(bi -> IssueDTO.fromEntity(bi.getCodeAnalysisIssue())) | ||
| .toList() | ||
| : List.of(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Move filtering and pagination to the database layer.
The current implementation fetches all branch issues into memory, then applies filters and pagination in Java. This approach has significant performance and scalability issues:
- All issues are loaded from the database regardless of filters
- Memory consumption grows linearly with the number of issues
- Filtering, sorting, and pagination happen after data transfer
- No benefit from database indexes on severity, category, filePath, dates, or resolved status
The TODO comment on line 150 acknowledges this limitation. Consider implementing repository methods that accept filter parameters and return paginated results directly from the database using Spring Data JPA Specifications or custom query methods.
💡 Alternative approach using repository layer
Add a method to BranchIssueRepository that accepts filter criteria:
// In BranchIssueRepository
Page<BranchIssue> findByBranchIdWithFilters(
Long branchId,
Boolean resolved,
String severity,
String category,
String filePath,
OffsetDateTime dateFrom,
OffsetDateTime dateTo,
Pageable pageable
);Or use Spring Data JPA Specifications for dynamic query building with type-safe filtering.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/analysis/controller/PullRequestController.java
around lines 150-211, the controller currently loads all BranchIssue rows into
memory and performs filtering, sorting and pagination in Java; instead implement
repository-level filtering and paging (e.g. add a repository method or a Spring
Data JPA Specification-based query that accepts branchId, resolved status
(mapped from "open"/"resolved"/"all"), severity, category, filePath (use
LOWER(...) LIKE for case-insensitive contains), dateFrom, dateTo and a Pageable)
and returns a Page<BranchIssue>, then call that repo method from the controller,
use the returned Page to obtain total, content and paging metadata, map the Page
content to IssueDTOs and remove the in-memory stream-based
filter/sort/pagination code; ensure null-safe parameter handling so omitted
filters are ignored and the DB query leverages indexes for performance.
…for repository interactions
feat: Add GitLab support with configuration, client, and API actions
…ality ( + re-apply webhook + small bugfix on branch issues count )
feat: Implement webhook management and VCS connection change functionalyty + bugfix
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 19
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/handler/CommentCommandWebhookHandler.java (1)
576-582: Remove outdated TODO comments.The TODO comments on Lines 576-577 indicate that user authorization is pending implementation. However, the authorization check has been implemented using
CommandAuthorizationServiceon Lines 578-582. Please remove these outdated TODO comments to avoid confusion.🔎 Proposed fix
- // TODO: Check if user is authorized - // TODO: For now, we check if the comment author is a workspace member String authorId = payload.commentData().commentAuthorId(); if (authorId != null && !isAuthorizedUser(project, authorId, payload)) { log.info("User {} not authorized for project {}", authorId, project.getId());
♻️ Duplicate comments (1)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.java (1)
307-312: GitHub merged PR detection issue already flagged.The condition
"pull_request.closed".equals(eventType)for GitHub detection was already identified in a previous review. GitHub sendsX-GitHub-Event: "pull_request"withaction: "closed"in the payload body, not"pull_request.closed"as the event type.
🟡 Minor comments (9)
java-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_gitlab_provider_type.sql-1-3 (1)
1-3: Complete the migration date.The date field contains a placeholder "2025-01-XX" that should be replaced with the actual migration creation date for proper documentation and tracking.
🔎 Proposed fix
-- Migration: Add GITLAB to vcs_connection provider_type CHECK constraint -- Description: Updates the CHECK constraint on vcs_connection.provider_type to include GITLAB --- Date: 2025-01-XX +-- Date: 2025-01-04java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/handler/GitLabMergeRequestWebhookHandler.java-241-246 (1)
241-246: Sanitize error messages before posting to VCS.Raw exception messages may contain sensitive information (internal paths, connection details, stack traces). Consider providing a generic user-facing message while logging the full details internally.
🔎 Proposed fix
private void updatePlaceholderWithError(Project project, Long mergeRequestIid, String commentId, String errorMessage) { try { VcsReportingService reportingService = vcsServiceFactory.getReportingService(EVcsProvider.GITLAB); - String errorContent = "⚠️ **CodeCrow Analysis Failed**\n\n" + errorMessage; + String sanitizedMessage = "An unexpected error occurred during analysis. Please check the logs or retry."; + log.error("MR analysis error details for MR {}: {}", mergeRequestIid, errorMessage); + String errorContent = "⚠️ **CodeCrow Analysis Failed**\n\n" + sanitizedMessage; reportingService.updateComment(project, mergeRequestIid, commentId, errorContent, CODECROW_ANALYSIS_MARKER); log.info("Updated placeholder comment {} with error message", commentId); } catch (Exception e) { log.error("Failed to update placeholder with error: {}", e.getMessage(), e); } }java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabReportingService.java-204-224 (1)
204-224: Fragile comment identification when marker is null.When
markeris null, the code falls back to searching by the first 50 characters of the comment content (line 223). This is brittle and could match the wrong comment if multiple comments share the same prefix. Consider requiring a marker or generating a unique one automatically.java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/webhook/GitLabWebhookParser.java-60-72 (1)
60-72: MR author extraction may be incorrect.The code extracts
mrAuthorUsernamefromobject_attributes.last_commit.author.name, which is the commit author, not the MR author. Additionally,userin the payload is the user who triggered the webhook event (may differ on MR updates).For the actual MR author, use
object_attributes.author_iddirectly:🔎 Proposed fix
commitHash = objectAttributes.path("last_commit").path("id").asText(null); - // Extract MR author - JsonNode author = objectAttributes.path("last_commit").path("author"); - if (!author.isMissingNode()) { - mrAuthorUsername = author.path("name").asText(null); - } - // Try user attribute - JsonNode user = payload.path("user"); - if (!user.isMissingNode()) { - mrAuthorId = String.valueOf(user.path("id").asLong()); - if (mrAuthorUsername == null) { - mrAuthorUsername = user.path("username").asText(null); - } - } + // Extract MR author from object_attributes + if (!objectAttributes.path("author_id").isMissingNode()) { + mrAuthorId = String.valueOf(objectAttributes.path("author_id").asLong()); + } + // Get username from user node (webhook sender, usually MR author on open) + JsonNode user = payload.path("user"); + if (!user.isMissingNode()) { + mrAuthorUsername = user.path("username").asText(null); + }java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/controller/gitlab/GitLabController.java-128-141 (1)
128-141: IOException should not leak from controller layer.Throwing
IOExceptionfrom a REST endpoint exposes implementation details and may result in a generic 500 error. Handle the exception internally and return a proper API error response.🔎 Proposed fix
@GetMapping("/{connectionId}/repositories") @PreAuthorize("@workspaceSecurity.hasOwnerOrAdminRights(#workspaceSlug, authentication)") public ResponseEntity<RepositorySearchResult> getGitLabConnectionRepositories( @PathVariable String workspaceSlug, @PathVariable Long connectionId, @RequestParam(value = "q", required = false) String query, @RequestParam(defaultValue = "1") int page - ) throws IOException { - Long workspaceId = workspaceService.getWorkspaceBySlug(workspaceSlug).getId(); - RepositorySearchResult repos = vcsConnectionService.searchGitLabRepositories( - workspaceId, connectionId, query, page); - - return new ResponseEntity<>(repos, HttpStatus.OK); + ) { + try { + Long workspaceId = workspaceService.getWorkspaceBySlug(workspaceSlug).getId(); + RepositorySearchResult repos = vcsConnectionService.searchGitLabRepositories( + workspaceId, connectionId, query, page); + return new ResponseEntity<>(repos, HttpStatus.OK); + } catch (IOException e) { + throw new IntegrationException("GITLAB_API_ERROR", "Failed to search GitLab repositories: " + e.getMessage()); + } }Committable suggestion skipped: line range outside the PR's diff.
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabClient.java-681-698 (1)
681-698: Incorrect semantic mapping:enable_ssl_verificationis not webhook active state.The
activefield should be derived from GitLab's webhook status, not SSL verification setting. GitLab webhooks have a separate enabled/disabled state that isn't captured here.🔎 Proposed fix
private VcsWebhook parseWebhook(JsonNode node) { String id = String.valueOf(node.get("id").asLong()); String url = getTextOrNull(node, "url"); - boolean active = !node.has("enable_ssl_verification") || node.get("enable_ssl_verification").asBoolean(); + // GitLab webhooks don't have an "active" field in API responses by default + // They're considered active unless explicitly disabled via alert_status or similar + boolean active = true;Committable suggestion skipped: line range outside the PR's diff.
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabOperationsService.java-32-36 (1)
32-36: PotentialNumberFormatExceptionon invalid MR IID.
Integer.parseInt(mergeRequestIid)will throw an uncheckedNumberFormatExceptionif the input is not a valid integer. This exception type differs from the declaredIOException, which may confuse callers.Consider wrapping the parse in a try-catch or validating input beforehand.
🔎 Proposed fix
@Override public String getPullRequestDiff(OkHttpClient client, String namespace, String project, String mergeRequestIid) throws IOException { GetMergeRequestDiffAction action = new GetMergeRequestDiffAction(client); - return action.getMergeRequestDiff(namespace, project, Integer.parseInt(mergeRequestIid)); + int mrIid; + try { + mrIid = Integer.parseInt(mergeRequestIid); + } catch (NumberFormatException e) { + throw new IOException("Invalid merge request IID: " + mergeRequestIid, e); + } + return action.getMergeRequestDiff(namespace, project, mrIid); }java-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabMcpClientImpl.java-64-76 (1)
64-76: Dead code:fileLimitloop has no effect.The loop iterating over changes and checking
fileLimit(lines 69-73) doesn't actually filter the results -changesis returned unchanged regardless ofcount. ThefileLimitis already applied inbuildUnifiedDiff, making this loop redundant.🔎 Proposed fix
@Override public List<FileDiffInfo> getPullRequestChanges() throws IOException { String diff = getMergeRequestDiff(config.getNamespace(), config.getProject(), config.getMrIid()); - List<FileDiffInfo> changes = parseDiff(diff); - - int count = 0; - for (FileDiffInfo change : changes) { - if (fileLimit > 0 && count >= fileLimit) break; - count++; - } - - return changes; + return parseDiff(diff); }java-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabMcpClientImpl.java-544-561 (1)
544-561: Potential NPEs inparseMergeRequestwhen fields are missing.Lines 546-547 assume
idandiidare always present. Line 556 accessesauthor.usernamewithout null-checking theauthornode. If GitLab returns unexpected data, this will throw NPEs.🔎 Proposed defensive fix
private Map<String, Object> parseMergeRequest(JsonNode node) { Map<String, Object> mr = new HashMap<>(); - mr.put("id", node.get("id").asLong()); - mr.put("iid", node.get("iid").asInt()); - mr.put("number", node.get("iid").asInt()); + mr.put("id", node.has("id") ? node.get("id").asLong() : null); + mr.put("iid", node.has("iid") ? node.get("iid").asInt() : null); + mr.put("number", node.has("iid") ? node.get("iid").asInt() : null); mr.put("title", getTextOrNull(node, "title")); mr.put("description", getTextOrNull(node, "description")); mr.put("state", getTextOrNull(node, "state")); mr.put("web_url", getTextOrNull(node, "web_url")); mr.put("html_url", getTextOrNull(node, "web_url")); mr.put("source_branch", getTextOrNull(node, "source_branch")); mr.put("target_branch", getTextOrNull(node, "target_branch")); - mr.put("author", node.has("author") ? node.get("author").get("username").asText() : null); + mr.put("author", node.has("author") && !node.get("author").isNull() && node.get("author").has("username") + ? node.get("author").get("username").asText() : null); mr.put("created_on", getTextOrNull(node, "created_at")); mr.put("updated_on", getTextOrNull(node, "updated_at")); mr.put("merged", "merged".equals(getTextOrNull(node, "state"))); return mr; }
🧹 Nitpick comments (25)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetMergeRequestAction.java (1)
37-42: Consider adding input validation for null parameters.If
namespaceorprojectis null,URLEncoder.encode()will throw aNullPointerExceptionwith no clear context. Adding defensive null checks would provide clearer error messages for callers.🔎 Proposed fix
public JsonNode getMergeRequest(String namespace, String project, int mergeRequestIid) throws IOException { + if (namespace == null || project == null) { + throw new IllegalArgumentException("namespace and project must not be null"); + } String projectPath = namespace + "/" + project; String encodedPath = URLEncoder.encode(projectPath, StandardCharsets.UTF_8);java-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_gitlab_provider_type.sql (1)
10-14: Consider alphabetical ordering of provider types.The constraint logic is correct and will successfully add GITLAB as a valid provider type. However, for consistency and maintainability, consider ordering the provider types alphabetically.
🔎 Proposed refactor
-- ===================================================== -- Step 2: Add updated CHECK constraint with GITLAB -- ===================================================== ALTER TABLE vcs_connection ADD CONSTRAINT vcs_connection_provider_type_check - CHECK (provider_type IN ('BITBUCKET_CLOUD', 'BITBUCKET_SERVER', 'GITHUB', 'GITLAB')); + CHECK (provider_type IN ('BITBUCKET_CLOUD', 'BITBUCKET_SERVER', 'GITHUB', 'GITLAB'));Wait, let me reconsider - they are already in alphabetical order (BITBUCKET_CLOUD, BITBUCKET_SERVER, GITHUB, GITLAB). Let me verify: B, B, G, G - yes, that's alphabetical. Actually, on second look, the ordering is already correct alphabetically.
Actually, reviewing the ordering again: BITBUCKET_CLOUD, BITBUCKET_SERVER, GITHUB, GITLAB - this is already in alphabetical order. My initial assessment was incorrect.
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/BranchService.java (1)
56-60: Consider consolidating multiple stream passes for better performance.The current implementation iterates through the issues list five times (once per count). For large issue lists, this could impact performance. Consider computing all counts in a single pass.
🔎 Proposed optimization
First, add the import at the top of the file:
+import org.rostilos.codecrow.core.model.codeanalysis.IssueSeverity;Then refactor the counting logic:
- // Calculate counts directly from issues list for accuracy - long openCount = issues.stream().filter(i -> !i.isResolved()).count(); - long resolvedCount = issues.stream().filter(BranchIssue::isResolved).count(); - long highCount = issues.stream().filter(i -> i.getSeverity() == org.rostilos.codecrow.core.model.codeanalysis.IssueSeverity.HIGH && !i.isResolved()).count(); - long mediumCount = issues.stream().filter(i -> i.getSeverity() == org.rostilos.codecrow.core.model.codeanalysis.IssueSeverity.MEDIUM && !i.isResolved()).count(); - long lowCount = issues.stream().filter(i -> i.getSeverity() == org.rostilos.codecrow.core.model.codeanalysis.IssueSeverity.LOW && !i.isResolved()).count(); + // Calculate counts directly from issues list for accuracy in a single pass + long openCount = 0, resolvedCount = 0, highCount = 0, mediumCount = 0, lowCount = 0; + for (BranchIssue issue : issues) { + if (issue.isResolved()) { + resolvedCount++; + } else { + openCount++; + IssueSeverity severity = issue.getSeverity(); + if (severity == IssueSeverity.HIGH) { + highCount++; + } else if (severity == IssueSeverity.MEDIUM) { + mediumCount++; + } else if (severity == IssueSeverity.LOW) { + lowCount++; + } + } + }java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabRepositoryTokenRequest.java (1)
35-36: Add URL format validation for baseUrl.The
baseUrlfield should validate that the provided string is a valid URL format to fail fast with a clear error message rather than during connection initialization.🔎 Proposed fix
Add the URL validation annotation:
+ import org.hibernate.validator.constraints.URL;Then apply it to the field:
/** * GitLab instance base URL for self-hosted instances. * Defaults to "https://gitlab.com" if not specified. */ + @URL(message = "Base URL must be a valid URL") @JsonProperty("baseUrl") private String baseUrl;java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/ValidateConnectionAction.java (1)
38-40: Consider using a lower log level for expected validation failures.The
WARNlog level might be too noisy for connection validation failures, which are expected in normal scenarios (e.g., invalid tokens, temporary network issues, initial setup). Consider usingINFOorDEBUGinstead, depending on whether this action is called frequently or only during initial configuration.🔎 Optional refactor
- log.warn("Failed to validate GitLab connection: {}", e.getMessage()); + log.debug("Failed to validate GitLab connection: {}", e.getMessage());Or if you want to preserve more context for troubleshooting:
- log.warn("Failed to validate GitLab connection: {}", e.getMessage()); + log.info("GitLab connection validation failed", e);java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabCreateRequest.java (2)
5-36: Consider adding validation annotations to ensure data integrity.Request DTOs typically benefit from validation annotations to ensure required fields are present and valid before processing. This prevents null/empty values from propagating through the system and provides clear error messages to API consumers.
Consider adding validation such as:
@NotBlankonaccessToken(likely required)@NotBlankor@NotNullonconnectionNameif required for connection creation- Validation on
groupIdbased on your business rules🔎 Example validation annotations
+import jakarta.validation.constraints.NotBlank; + public class GitLabCreateRequest { @JsonProperty("accessToken") + @NotBlank(message = "Access token is required") private String accessToken; + @NotBlank(message = "Group ID is required") private String groupId; + @NotBlank(message = "Connection name is required") private String connectionName;Adjust based on which fields are truly required for your use case.
7-12: Consider consistent @JsonProperty usage across all fields.Only
accessTokenhas the@JsonPropertyannotation, whilegroupIdandconnectionNamedo not. If the JSON property names match the field names exactly for the latter two, this works correctly. However, for consistency and maintainability, consider either:
- Adding
@JsonPropertyto all fields for explicit JSON mapping- Removing it from
accessTokenif no mapping is neededConsistent annotation usage makes the contract clearer and prevents future mistakes.
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/handler/GitLabMergeRequestWebhookHandler.java (2)
53-59: Consider adding null checks for constructor parameters.While Spring manages these dependencies, adding defensive null checks with
Objects.requireNonNull()would provide clearer fail-fast behavior if misconfigured.🔎 Proposed fix
+import java.util.Objects; + public GitLabMergeRequestWebhookHandler( PullRequestAnalysisProcessor pullRequestAnalysisProcessor, VcsServiceFactory vcsServiceFactory ) { - this.pullRequestAnalysisProcessor = pullRequestAnalysisProcessor; - this.vcsServiceFactory = vcsServiceFactory; + this.pullRequestAnalysisProcessor = Objects.requireNonNull(pullRequestAnalysisProcessor, "pullRequestAnalysisProcessor must not be null"); + this.vcsServiceFactory = Objects.requireNonNull(vcsServiceFactory, "vcsServiceFactory must not be null"); }
155-160: Consider validatingpullRequestIdbefore parsing.
Long.parseLong(payload.pullRequestId())is called multiple times and will throwNumberFormatExceptionif the value is null or non-numeric. While caught by the outer try-catch, validating upfront would provide clearer error messages.🔎 Proposed fix
private WebhookResult handleMergeRequestEvent( WebhookPayload payload, Project project, Consumer<Map<String, Object>> eventConsumer ) { String placeholderCommentId = null; + + Long mergeRequestId; + try { + mergeRequestId = Long.parseLong(payload.pullRequestId()); + } catch (NumberFormatException e) { + return WebhookResult.error("Invalid MR ID: " + payload.pullRequestId()); + } try { // Post placeholder comment immediately to show analysis has started - placeholderCommentId = postPlaceholderComment(project, Long.parseLong(payload.pullRequestId())); + placeholderCommentId = postPlaceholderComment(project, mergeRequestId); // Convert WebhookPayload to PrProcessRequest PrProcessRequest request = new PrProcessRequest(); request.projectId = project.getId(); - request.pullRequestId = Long.parseLong(payload.pullRequestId()); + request.pullRequestId = mergeRequestId;java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabReportingService.java (2)
75-75: Reconsider @transactional(readOnly = true) for methods making external HTTP calls.These methods are annotated with
@Transactional(readOnly = true), but they perform external HTTP operations viapostOrUpdateComment. Read-only transactions provide no consistency guarantees for external HTTP calls and add unnecessary overhead. Unless these methods access database entities that need transaction isolation, consider removing the transaction annotation.Also applies to: 86-86
204-208: Extract duplicated marker-addition logic.The logic for appending markers to content is duplicated in
postComment(lines 204-208) andupdateComment(lines 349-353). Consider extracting this into a private helper method.🔎 Proposed refactor
+ private String addMarkerToContent(String content, String marker) { + if (marker != null && !marker.isBlank()) { + return content + "\n\n" + marker; + } + return content; + } + @Override public String postComment( Project project, Long mergeRequestIid, String content, String marker ) throws IOException { VcsRepoInfo vcsRepoInfo = getVcsRepoInfo(project); OkHttpClient httpClient = vcsClientProvider.getHttpClient(vcsRepoInfo.getVcsConnection()); CommentOnMergeRequestAction commentAction = new CommentOnMergeRequestAction(httpClient); - // Add marker at the END as HTML comment (invisible to users) if provided - String markedContent = content; - if (marker != null && !marker.isBlank()) { - markedContent = content + "\n\n" + marker; - } + String markedContent = addMarkerToContent(content, marker); // Post the comment commentAction.postComment( vcsRepoInfo.getRepoWorkspace(), vcsRepoInfo.getRepoSlug(), mergeRequestIid.intValue(), markedContent ); // ... rest of method } @Override public void updateComment( Project project, Long mergeRequestIid, String commentId, String newContent, String marker ) throws IOException { VcsRepoInfo vcsRepoInfo = getVcsRepoInfo(project); OkHttpClient httpClient = vcsClientProvider.getHttpClient(vcsRepoInfo.getVcsConnection()); CommentOnMergeRequestAction commentAction = new CommentOnMergeRequestAction(httpClient); - // Add marker at the END as HTML comment (invisible to users) if provided - String markedContent = newContent; - if (marker != null && !marker.isBlank()) { - markedContent = newContent + "\n\n" + marker; - } + String markedContent = addMarkerToContent(newContent, marker); // ... rest of method }Also applies to: 349-353
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/controller/gitlab/GitLabController.java (3)
35-36: Inconsistent dependency usage: direct repository access alongside service.The controller injects both
VcsConnectionRepositoryandVcsConnectionWebService, butgetConnectionInfo()(line 118) bypasses the service layer to access the repository directly. Consider routing all operations through the service for consistency and to centralize business logic.
100-109: Unusual return type and missing response body.
ResponseEntity<HttpStatus>is unconventional. UseResponseEntity<Void>for no-content responses, or return a confirmation message. Also, HTTP 204 No Content is more appropriate for successful deletions.🔎 Proposed fix
@DeleteMapping("/connections/{connectionId}") @PreAuthorize("@workspaceSecurity.hasOwnerOrAdminRights(#workspaceSlug, authentication)") - public ResponseEntity<HttpStatus> deleteGitLabConnection( + public ResponseEntity<Void> deleteGitLabConnection( @PathVariable String workspaceSlug, @PathVariable Long connectionId ) { Long workspaceId = workspaceService.getWorkspaceBySlug(workspaceSlug).getId(); vcsConnectionService.deleteGitLabConnection(workspaceId, connectionId); - return new ResponseEntity<>(HttpStatus.OK); + return ResponseEntity.noContent().build(); }
117-126: Use proper HTTP exceptions instead of IllegalArgumentException.
IllegalArgumentExceptionwill result in a 500 error. Use appropriate exceptions that map to correct HTTP status codes (404 for not found, 400 for wrong type).🔎 Proposed fix
VcsConnection connection = vcsConnectionRepository.findByWorkspace_IdAndId(workspaceId, connectionId) - .orElseThrow(() -> new IllegalArgumentException("VCS connection not found")); + .orElseThrow(() -> new IntegrationException("CONNECTION_NOT_FOUND", "VCS connection not found")); if (connection.getProviderType() != EVcsProvider.GITLAB) { - throw new IllegalArgumentException("Not a GitLab connection"); + throw new IntegrationException("INVALID_PROVIDER", "Not a GitLab connection"); }java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CommentOnMergeRequestAction.java (1)
100-121: Pagination limitation: hardcodedper_page=100may miss notes on busy MRs.The
listNotesmethod fetches at most 100 notes. MRs with extensive discussion could exceed this limit, causingfindCommentByMarkerto miss existing markers. Consider implementing pagination similar to the pattern inGitLabClient.listWebhooks.java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/service/VcsConnectionWebService.java (2)
452-466: Consider extracting repository-to-map conversion to a reusable mapper.The manual
Map<String, Object>construction is verbose. A dedicated mapper or builder method would improve maintainability and reduce duplication if similar conversions are needed elsewhere.
491-498: Edge case: namespace extraction may produce unexpected results for malformed paths.If
repositoryPathis"/"or ends with"/", the extraction logic could produce empty strings or incorrect values. Consider adding validation for the repository path format.🔎 Proposed validation
// Extract namespace from repository path (e.g., "rostilos/codecrow-sample" -> "rostilos") String repositoryPath = request.getRepositoryPath(); + if (repositoryPath == null || repositoryPath.isBlank() || !repositoryPath.contains("/")) { + throw new IllegalArgumentException("Invalid repository path format. Expected 'namespace/project'"); + } String namespace = repositoryPath.contains("/") ? repositoryPath.substring(0, repositoryPath.lastIndexOf("/")) : repositoryPath; + if (namespace.isBlank()) { + throw new IllegalArgumentException("Invalid repository path: namespace cannot be empty"); + } String repoSlug = repositoryPath.contains("/") ? repositoryPath.substring(repositoryPath.lastIndexOf("/") + 1) : repositoryPath; + if (repoSlug.isBlank()) { + throw new IllegalArgumentException("Invalid repository path: repository name cannot be empty"); + }java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabClient.java (1)
569-576: Performance concern:isCurrentUsermakes an HTTP call on every invocation.This method is called in
listRepositoriesandsearchRepositories, potentially causing redundant API calls. Consider caching the current user or passing it as context.🔎 Proposed caching approach
+ private volatile VcsUser cachedCurrentUser; + private boolean isCurrentUser(String workspaceId) { try { - VcsUser currentUser = getCurrentUser(); + if (cachedCurrentUser == null) { + cachedCurrentUser = getCurrentUser(); + } + VcsUser currentUser = cachedCurrentUser; return currentUser != null && currentUser.username().equalsIgnoreCase(workspaceId); } catch (IOException e) { return false; } }java-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabConfiguration.java (1)
6-35: Consider converting to a Java record for conciseness.This class is a simple immutable data holder. Converting it to a record would reduce boilerplate and automatically provide
equals(),hashCode(), andtoString()methods.🔎 Proposed record conversion
-public class GitLabConfiguration { - - private final String accessToken; - private final String namespace; - private final String project; - private final String mrIid; - - public GitLabConfiguration(String accessToken, String namespace, String project, String mrIid) { - this.accessToken = accessToken; - this.namespace = namespace; - this.project = project; - this.mrIid = mrIid; - } - - public String getAccessToken() { - return accessToken; - } - - public String getNamespace() { - return namespace; - } - - public String getProject() { - return project; - } - - public String getMrIid() { - return mrIid; - } -} +public record GitLabConfiguration( + String accessToken, + String namespace, + String project, + String mrIid +) { + // Add validation in compact constructor if needed +}java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/vcs/config/gitlab/GitLabConfig.java (1)
13-18: Consider null safety for allowedRepos.The
allowedReposparameter could be null, which might cause NullPointerExceptions when accessed. Consider adding a compact constructor to validate and/or default to an empty list.🔎 Proposed compact constructor
@JsonTypeName("gitlab") public record GitLabConfig( String accessToken, String groupId, List<String> allowedRepos, String baseUrl // For self-hosted GitLab instances (e.g., "https://gitlab.mycompany.com") ) implements VcsConnectionConfig { + + public GitLabConfig { + // Compact constructor for validation + if (allowedRepos == null) { + allowedRepos = List.of(); + } + }java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/gitlab/GitLabDTO.java (1)
27-52: Reduce code duplication in DTO construction.The two DTO construction blocks have significant overlap. Consider extracting common logic to improve maintainability.
🔎 Proposed refactor
- if (vcsConnection.getConnectionType() == EVcsConnectionType.APP || vcsConnection.getConfiguration() == null) { - return new GitLabDTO( - vcsConnection.getId(), - vcsConnection.getConnectionName(), - vcsConnection.getExternalWorkspaceSlug(), - vcsConnection.getRepoCount(), - vcsConnection.getSetupStatus(), - vcsConnection.getAccessToken() != null && !vcsConnection.getAccessToken().isBlank(), - vcsConnection.getUpdatedAt(), - vcsConnection.getConnectionType(), - vcsConnection.getRepositoryPath() - ); - } + String groupId; + boolean hasAccessToken; - GitLabConfig config = (GitLabConfig) vcsConnection.getConfiguration(); - return new GitLabDTO( - vcsConnection.getId(), - vcsConnection.getConnectionName(), - config.groupId(), - vcsConnection.getRepoCount(), - vcsConnection.getSetupStatus(), - config.accessToken() != null && !config.accessToken().isBlank(), - vcsConnection.getUpdatedAt(), - vcsConnection.getConnectionType(), - vcsConnection.getRepositoryPath() - ); + if (vcsConnection.getConnectionType() == EVcsConnectionType.APP || vcsConnection.getConfiguration() == null) { + groupId = vcsConnection.getExternalWorkspaceSlug(); + hasAccessToken = vcsConnection.getAccessToken() != null && !vcsConnection.getAccessToken().isBlank(); + } else { + if (!(vcsConnection.getConfiguration() instanceof GitLabConfig config)) { + throw new IllegalArgumentException("Expected GitLabConfig configuration"); + } + groupId = config.groupId(); + hasAccessToken = config.accessToken() != null && !config.accessToken().isBlank(); + } + + return new GitLabDTO( + vcsConnection.getId(), + vcsConnection.getConnectionName(), + groupId, + vcsConnection.getRepoCount(), + vcsConnection.getSetupStatus(), + hasAccessToken, + vcsConnection.getUpdatedAt(), + vcsConnection.getConnectionType(), + vcsConnection.getRepositoryPath() + );java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitRangeDiffAction.java (2)
68-77: Avoid instantiatingObjectMapperon every call.Creating a new
ObjectMapperinsidebuildUnifiedDifffor each invocation is inefficient. Jackson'sObjectMapperis thread-safe and should be reused.Additionally, consider adding proper imports instead of using fully-qualified class names.
🔎 Proposed fix
Add imports at the top of the file:
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper;Add a static field:
public class GetCommitRangeDiffAction { private static final Logger log = LoggerFactory.getLogger(GetCommitRangeDiffAction.class); + private static final ObjectMapper objectMapper = new ObjectMapper(); private final OkHttpClient authorizedOkHttpClient;Then update
buildUnifiedDiff:private String buildUnifiedDiff(String responseBody) throws IOException { StringBuilder combinedDiff = new StringBuilder(); - com.fasterxml.jackson.databind.ObjectMapper objectMapper = new com.fasterxml.jackson.databind.ObjectMapper(); - com.fasterxml.jackson.databind.JsonNode root = objectMapper.readTree(responseBody); - com.fasterxml.jackson.databind.JsonNode diffs = root.get("diffs"); + JsonNode root = objectMapper.readTree(responseBody); + JsonNode diffs = root.get("diffs");
79-118: Consider extracting shared diff-building logic.The
buildUnifiedDiffimplementation is nearly identical acrossGetCommitRangeDiffAction,GetCommitDiffAction, andGetMergeRequestDiffAction. Consider extracting this to a shared utility class to reduce duplication and simplify maintenance.java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/SearchRepositoriesAction.java (1)
27-29: MakeObjectMapperstatic and consider usingGitLabConfig.DEFAULT_PAGE_SIZE.
ObjectMappershould be a static field since it's thread-safe and expensive to create.PAGE_SIZE(30) differs fromGitLabConfig.DEFAULT_PAGE_SIZE(20). Consider using the config constant for consistency.🔎 Proposed fix
public class SearchRepositoriesAction { private static final Logger log = LoggerFactory.getLogger(SearchRepositoriesAction.class); - private static final int PAGE_SIZE = 30; + private static final int PAGE_SIZE = GitLabConfig.DEFAULT_PAGE_SIZE; + private static final ObjectMapper objectMapper = new ObjectMapper(); private final OkHttpClient authorizedOkHttpClient; - private final ObjectMapper objectMapper = new ObjectMapper();java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/handler/GitLabBranchWebhookHandler.java (1)
85-98: Consider using Optional or custom result type for validation.Returning
nullfor success and aStringfor error is functional but unconventional. A more idiomatic approach would useOptional<String>for the error or a dedicated validation result type. This is a minor stylistic concern.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (48)
deployment/config/java-shared/application.properties.sampledocs/architecture/mcp-scaling-strategy.mdjava-ecosystem/libs/core/src/main/java/module-info.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/gitlab/GitLabDTO.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/project/ProjectDTO.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/vcs/EVcsConnectionType.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/vcs/VcsConnection.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/vcs/config/VcsConnectionConfig.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/vcs/config/gitlab/GitLabConfig.javajava-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/service/BranchService.javajava-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__add_gitlab_provider_type.sqljava-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__backfill_code_analysis_commit_hash.sqljava-ecosystem/libs/core/src/main/resources/db/migration/0.2.0/V0.2.0__remove_forge_app_vsc_connection_type_and_add_repository_token.sqljava-ecosystem/libs/vcs-client/src/main/java/module-info.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/HttpAuthorizedClientFactory.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientFactory.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabClient.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabException.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CheckFileExistsInBranchAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CommentOnMergeRequestAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitDiffAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitRangeDiffAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetMergeRequestAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetMergeRequestDiffAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/SearchRepositoriesAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/ValidateConnectionAction.javajava-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/dto/response/RepositorySearchResult.javajava-ecosystem/mcp-servers/platform-mcp/src/main/java/org/rostilos/codecrow/platformmcp/service/VcsService.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabClientFactory.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabConfiguration.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabException.javajava-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabMcpClientImpl.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/controller/ProviderWebhookController.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/generic/handler/CommentCommandWebhookHandler.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/handler/GitLabBranchWebhookHandler.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/handler/GitLabMergeRequestWebhookHandler.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabAiClientService.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabOperationsService.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabReportingService.javajava-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/webhook/GitLabWebhookParser.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/integration/controller/OAuthCallbackController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/integration/service/VcsIntegrationService.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/controller/gitlab/GitLabController.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabCreateRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabRepositoryTokenRequest.javajava-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/service/VcsConnectionWebService.java
✅ Files skipped from review due to trivial changes (1)
- java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java
🧰 Additional context used
🧬 Code graph analysis (16)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/controller/gitlab/GitLabController.java (3)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabCreateRequest.java (1)
GitLabCreateRequest(5-37)java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabRepositoryTokenRequest.java (1)
GitLabRepositoryTokenRequest(10-69)
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/vcs/config/gitlab/GitLabConfig.java (2)
python-ecosystem/rag-pipeline/src/rag_pipeline/core/openrouter_embedding.py (1)
model(96-98)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabReportingService.java (2)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CommentOnMergeRequestAction.java (1)
CommentOnMergeRequestAction(20-189)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/VcsClientProvider.java (1)
Service(51-670)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetMergeRequestDiffAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/SearchRepositoriesAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabAiClientService.java (5)
java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/dto/request/ai/AiAnalysisRequestImpl.java (1)
AiAnalysisRequestImpl(14-365)java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/util/DiffContentFilter.java (1)
DiffContentFilter(18-179)java-ecosystem/libs/analysis-engine/src/main/java/org/rostilos/codecrow/analysisengine/util/DiffParser.java (1)
DiffParser(11-189)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitRangeDiffAction.java (1)
GetCommitRangeDiffAction(17-122)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetMergeRequestDiffAction.java (1)
GetMergeRequestDiffAction(19-121)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitRangeDiffAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CommentOnMergeRequestAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CheckFileExistsInBranchAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetMergeRequestAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabException.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabException.java (1)
GitLabException(6-36)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitDiffAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabOperationsService.java (4)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CheckFileExistsInBranchAction.java (1)
CheckFileExistsInBranchAction(17-62)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitDiffAction.java (1)
GetCommitDiffAction(19-119)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetCommitRangeDiffAction.java (1)
GetCommitRangeDiffAction(17-122)java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/GetMergeRequestDiffAction.java (1)
GetMergeRequestDiffAction(19-121)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/ValidateConnectionAction.java (1)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabConfig.java (1)
GitLabConfig(6-14)
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/GitLabException.java (1)
java-ecosystem/mcp-servers/vcs-mcp/src/main/java/org/rostilos/codecrow/mcp/gitlab/GitLabException.java (1)
GitLabException(6-15)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/integration/service/VcsIntegrationService.java (1)
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/integration/dto/request/RepoOnboardRequest.java (1)
RepoOnboardRequest(8-124)
| # Email templates frontend base url | ||
| codecrow.frontend.url=http://localhost:8080 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consolidate frontend URL properties to avoid configuration ambiguity.
This introduces a third property for the frontend URL with inconsistent naming:
- Line 24:
codecrow.frontend-url(dash notation) - Line 46:
codecrow.frontend.url(dot notation, NEW) - Line 56:
codecrow.email.frontend-url(dash notation)
The inconsistent naming convention (dash vs. dot) creates ambiguity about which property should be used in different contexts and increases the risk of misconfiguration.
🔎 Recommended approach
Consider consolidating to a single canonical property:
-# Email templates frontend base url
-codecrow.frontend.url=http://localhost:8080
-Then reference the existing codecrow.frontend-url (line 24) throughout the codebase, or establish a clear convention (all dots or all dashes) and update all properties consistently.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Email templates frontend base url | |
| codecrow.frontend.url=http://localhost:8080 |
🤖 Prompt for AI Agents
In deployment/config/java-shared/application.properties.sample around lines
45–46, there is a duplicate frontend URL property using dot notation
(codecrow.frontend.url) that conflicts with existing dash-notated properties
(codecrow.frontend-url at line 24 and codecrow.email.frontend-url at line 56);
remove the new codecrow.frontend.url entry and consolidate to a single canonical
key (use the existing codecrow.frontend-url), update codecrow.email.frontend-url
to reference or be renamed consistently to codecrow.frontend-url (or vice versa
if you choose a different canonical name), and then search the codebase/configs
to update any references to the old variants so all configs use the single
canonical property name.
| ); | ||
| } | ||
|
|
||
| GitLabConfig config = (GitLabConfig) vcsConnection.getConfiguration(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add instanceof check before casting to GitLabConfig.
The cast to GitLabConfig is unsafe. If vcsConnection.getConfiguration() returns a different implementation of VcsConnectionConfig, this will throw a ClassCastException. Add an instanceof check before casting.
🔎 Proposed fix
+ if (!(vcsConnection.getConfiguration() instanceof GitLabConfig)) {
+ throw new IllegalArgumentException("Expected GitLabConfig configuration");
+ }
+
GitLabConfig config = (GitLabConfig) vcsConnection.getConfiguration();🤖 Prompt for AI Agents
In
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/dto/gitlab/GitLabDTO.java
around line 41, the code performs an unsafe cast: GitLabConfig config =
(GitLabConfig) vcsConnection.getConfiguration(); — add an instanceof check
before casting to ensure the returned configuration is a GitLabConfig; if it is,
cast and proceed, otherwise handle the mismatch (throw a clear
IllegalArgumentException or log and skip/return) to avoid ClassCastException at
runtime.
| public String effectiveBaseUrl() { | ||
| return (baseUrl != null && !baseUrl.isBlank()) ? baseUrl : "https://gitlab.com"; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Validate baseUrl format to prevent malformed URLs.
The effectiveBaseUrl() method returns baseUrl without validating its format. If baseUrl contains an invalid URL (e.g., missing scheme, malformed host), it could cause failures when constructing GitLab API endpoints.
🔎 Proposed validation
public String effectiveBaseUrl() {
- return (baseUrl != null && !baseUrl.isBlank()) ? baseUrl : "https://gitlab.com";
+ if (baseUrl == null || baseUrl.isBlank()) {
+ return "https://gitlab.com";
+ }
+ // Basic validation: ensure it looks like a valid URL
+ if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) {
+ throw new IllegalStateException("baseUrl must start with http:// or https://");
+ }
+ return baseUrl;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public String effectiveBaseUrl() { | |
| return (baseUrl != null && !baseUrl.isBlank()) ? baseUrl : "https://gitlab.com"; | |
| } | |
| public String effectiveBaseUrl() { | |
| if (baseUrl == null || baseUrl.isBlank()) { | |
| return "https://gitlab.com"; | |
| } | |
| // Basic validation: ensure it looks like a valid URL | |
| if (!baseUrl.startsWith("http://") && !baseUrl.startsWith("https://")) { | |
| throw new IllegalStateException("baseUrl must start with http:// or https://"); | |
| } | |
| return baseUrl; | |
| } |
🤖 Prompt for AI Agents
In
java-ecosystem/libs/core/src/main/java/org/rostilos/codecrow/core/model/vcs/config/gitlab/GitLabConfig.java
around lines 30-32, the effectiveBaseUrl() currently returns baseUrl without
validation; update it to validate and normalize baseUrl before returning: trim
the value, attempt to parse it as a java.net.URI or URL, ensure it has a
supported scheme (http or https) and a non-empty host, and if it lacks a scheme
consider prepending "https://" and re-validate; if validation fails return the
default "https://gitlab.com" (or throw IllegalArgumentException if you prefer
fail-fast), and ensure no malformed URL can be returned by this method.
| public void postLineComment(String namespace, String project, int mergeRequestIid, | ||
| String body, String baseSha, String headSha, String startSha, | ||
| String filePath, int newLine) throws IOException { | ||
| String projectPath = namespace + "/" + project; | ||
| String encodedPath = URLEncoder.encode(projectPath, StandardCharsets.UTF_8); | ||
| String apiUrl = String.format("%s/projects/%s/merge_requests/%d/discussions", | ||
| GitLabConfig.API_BASE, encodedPath, mergeRequestIid); | ||
|
|
||
| Map<String, Object> position = new HashMap<>(); | ||
| position.put("base_sha", baseSha); | ||
| position.put("head_sha", headSha); | ||
| position.put("start_sha", startSha); | ||
| position.put("position_type", "text"); | ||
| position.put("new_path", filePath); | ||
| position.put("new_line", newLine); | ||
|
|
||
| Map<String, Object> payload = new HashMap<>(); | ||
| payload.put("body", body); | ||
| payload.put("position", position); | ||
|
|
||
| Request req = new Request.Builder() | ||
| .url(apiUrl) | ||
| .header("Accept", "application/json") | ||
| .post(RequestBody.create(objectMapper.writeValueAsString(payload), JSON)) | ||
| .build(); | ||
|
|
||
| try (Response resp = authorizedOkHttpClient.newCall(req).execute()) { | ||
| if (!resp.isSuccessful()) { | ||
| String respBody = resp.body() != null ? resp.body().string() : ""; | ||
| log.warn("Failed to post line comment: {} - {}", resp.code(), respBody); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent error handling: postLineComment silently swallows failures.
Unlike postComment (which throws IOException on non-success responses), postLineComment only logs a warning and returns silently on failure. This inconsistency could mask errors when posting inline comments.
🔎 Proposed fix to throw on failure
try (Response resp = authorizedOkHttpClient.newCall(req).execute()) {
if (!resp.isSuccessful()) {
String respBody = resp.body() != null ? resp.body().string() : "";
- log.warn("Failed to post line comment: {} - {}", resp.code(), respBody);
+ String msg = String.format("Failed to post line comment: %d - %s", resp.code(), respBody);
+ log.warn(msg);
+ throw new IOException(msg);
}
}🤖 Prompt for AI Agents
In
java-ecosystem/libs/vcs-client/src/main/java/org/rostilos/codecrow/vcsclient/gitlab/actions/CommentOnMergeRequestAction.java
around lines 63 to 95, postLineComment currently logs a warning and returns on
non-success HTTP responses; change it to mirror postComment by throwing an
IOException when the response is not successful. After reading resp.body()
safely into a string (handling null), throw new
IOException(String.format("Failed to post line comment: %d - %s", resp.code(),
respBody)); keep the try-with-resources so the response is closed and do not
swallow the exception.
| public String getMergeRequestDiff(String namespace, String project, int mergeRequestIid) throws IOException { | ||
| String projectPath = namespace + "/" + project; | ||
| String encodedPath = URLEncoder.encode(projectPath, StandardCharsets.UTF_8); | ||
|
|
||
| // First, get the merge request changes (diffs) | ||
| String apiUrl = String.format("%s/projects/%s/merge_requests/%d/changes", | ||
| GitLabConfig.API_BASE, encodedPath, mergeRequestIid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Self-hosted GitLab instances not supported.
The API URL is hardcoded to GitLabConfig.API_BASE which points to https://gitlab.com/api/v4. This action won't work for self-hosted GitLab instances. Consider passing the base URL as a parameter or through the authorized client's configuration.
🔎 Proposed fix
- public String getMergeRequestDiff(String namespace, String project, int mergeRequestIid) throws IOException {
+ public String getMergeRequestDiff(String baseUrl, String namespace, String project, int mergeRequestIid) throws IOException {
String projectPath = namespace + "/" + project;
String encodedPath = URLEncoder.encode(projectPath, StandardCharsets.UTF_8);
// First, get the merge request changes (diffs)
String apiUrl = String.format("%s/projects/%s/merge_requests/%d/changes",
- GitLabConfig.API_BASE, encodedPath, mergeRequestIid);
+ baseUrl != null ? baseUrl : GitLabConfig.API_BASE, encodedPath, mergeRequestIid);Committable suggestion skipped: line range outside the PR's diff.
| .withDiffSnippets(diffSnippets) | ||
| .withRawDiff(rawDiff) | ||
| .withProjectMetadata(project.getWorkspace().getName(), project.getNamespace()) | ||
| .withTargetBranchName(request.targetBranchName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix method call for targetBranchName.
Line 224 uses direct field access request.targetBranchName instead of a method call. Based on the usage pattern elsewhere in this method (e.g., Line 128 uses request.getPullRequestId()), this should be a getter method call.
🔎 Proposed fix
- .withTargetBranchName(request.targetBranchName)
+ .withTargetBranchName(request.getTargetBranchName())📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .withTargetBranchName(request.targetBranchName) | |
| .withTargetBranchName(request.getTargetBranchName()) |
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabAiClientService.java
around line 224, the builder call uses direct field access
request.targetBranchName instead of the class's getter; replace the field access
with the appropriate getter (request.getTargetBranchName()) to match the rest of
the method's usage pattern and ensure proper encapsulation.
| vcsRepoInfo.getRepoWorkspace(), | ||
| vcsRepoInfo.getRepoSlug(), | ||
| mergeRequestIid.intValue(), | ||
| Long.parseLong(placeholderCommentId), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validation before parsing placeholderCommentId.
The placeholderCommentId is parsed directly without validation, which can throw NumberFormatException if the ID is malformed. Consider adding validation or catching the specific exception.
🔎 Proposed fix
- commentAction.updateNote(
- vcsRepoInfo.getRepoWorkspace(),
- vcsRepoInfo.getRepoSlug(),
- mergeRequestIid.intValue(),
- Long.parseLong(placeholderCommentId),
- markedComment
- );
+ try {
+ long noteId = Long.parseLong(placeholderCommentId);
+ commentAction.updateNote(
+ vcsRepoInfo.getRepoWorkspace(),
+ vcsRepoInfo.getRepoSlug(),
+ mergeRequestIid.intValue(),
+ noteId,
+ markedComment
+ );
+ } catch (NumberFormatException e) {
+ throw new IOException("Invalid placeholder comment ID: " + placeholderCommentId, e);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Long.parseLong(placeholderCommentId), | |
| try { | |
| long noteId = Long.parseLong(placeholderCommentId); | |
| commentAction.updateNote( | |
| vcsRepoInfo.getRepoWorkspace(), | |
| vcsRepoInfo.getRepoSlug(), | |
| mergeRequestIid.intValue(), | |
| noteId, | |
| markedComment | |
| ); | |
| } catch (NumberFormatException e) { | |
| throw new IOException("Invalid placeholder comment ID: " + placeholderCommentId, e); | |
| } |
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabReportingService.java
around line 134, the code calls Long.parseLong(placeholderCommentId) without
validation which can throw NumberFormatException; add a guard to check for
null/blank and wrap the parse in a try-catch for NumberFormatException, handling
the invalid value by logging a clear warning/error (including the raw
placeholderCommentId and context) and skipping or returning early (or rethrowing
a controlled exception) instead of letting the method throw an uncaught runtime
exception.
| vcsRepoInfo.getRepoWorkspace(), | ||
| vcsRepoInfo.getRepoSlug(), | ||
| mergeRequestIid.intValue(), | ||
| Long.parseLong(commentId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add validation before parsing commentId.
Both deleteComment and updateComment parse commentId without validation, risking NumberFormatException. Add validation or wrap in try-catch to handle malformed IDs gracefully.
🔎 Proposed fix for deleteComment
- commentAction.deleteNote(
- vcsRepoInfo.getRepoWorkspace(),
- vcsRepoInfo.getRepoSlug(),
- mergeRequestIid.intValue(),
- Long.parseLong(commentId)
- );
+ try {
+ long noteId = Long.parseLong(commentId);
+ commentAction.deleteNote(
+ vcsRepoInfo.getRepoWorkspace(),
+ vcsRepoInfo.getRepoSlug(),
+ mergeRequestIid.intValue(),
+ noteId
+ );
+ } catch (NumberFormatException e) {
+ throw new IOException("Invalid comment ID: " + commentId, e);
+ }🔎 Proposed fix for updateComment
- commentAction.updateNote(
- vcsRepoInfo.getRepoWorkspace(),
- vcsRepoInfo.getRepoSlug(),
- mergeRequestIid.intValue(),
- Long.parseLong(commentId),
- markedContent
- );
+ try {
+ long noteId = Long.parseLong(commentId);
+ commentAction.updateNote(
+ vcsRepoInfo.getRepoWorkspace(),
+ vcsRepoInfo.getRepoSlug(),
+ mergeRequestIid.intValue(),
+ noteId,
+ markedContent
+ );
+ } catch (NumberFormatException e) {
+ throw new IOException("Invalid comment ID: " + commentId, e);
+ }Also applies to: 359-359
🤖 Prompt for AI Agents
In
java-ecosystem/services/pipeline-agent/src/main/java/org/rostilos/codecrow/pipelineagent/gitlab/service/GitLabReportingService.java
around lines 332 and 359, the code calls Long.parseLong(commentId) without
validating the input which can throw NumberFormatException; wrap the parse in a
try-catch for NumberFormatException (or pre-validate with a regex/isNumeric
check), handle malformed IDs by logging a clear warning and returning early (or
returning an appropriate error/optional) instead of allowing the exception to
propagate, and ensure both deleteComment and updateComment follow the same safe
parsing pattern.
| @JsonProperty("accessToken") | ||
| private String accessToken; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Protect the sensitive accessToken from accidental logging.
The accessToken field stores sensitive credentials but lacks protection against accidental exposure. If this DTO is logged (e.g., via a default toString() implementation in a parent class, debug logging, or error handlers), the token will be leaked.
Consider:
- Override
toString()to exclude or mask theaccessTokenfield - Add
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)if the token should never be serialized in responses - Add validation (e.g.,
@NotBlank) to ensure tokens are never null or empty
🔎 Proposed fix to protect the access token
public class GitLabCreateRequest {
- @JsonProperty("accessToken")
+ @JsonProperty(value = "accessToken", access = JsonProperty.Access.WRITE_ONLY)
+ @NotBlank(message = "Access token is required")
private String accessToken;
private String groupId;
private String connectionName;
+
+ @Override
+ public String toString() {
+ return "GitLabCreateRequest{" +
+ "accessToken='[REDACTED]', " +
+ "groupId='" + groupId + "', " +
+ "connectionName='" + connectionName + "'" +
+ "}";
+ }Note: You'll need to add jakarta.validation.constraints.NotBlank or javax.validation.constraints.NotBlank import depending on your validation framework.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabCreateRequest.java
around lines 7-8, the accessToken field is currently exposed and may be leaked
by default toString/serialization; protect it by marking the field write-only
for JSON (add @JsonProperty(value = "accessToken", access =
JsonProperty.Access.WRITE_ONLY)), add validation such as @NotBlank to ensure
it’s not null/empty (import the appropriate jakarta or javax validation
annotation), and override the DTO’s toString() to either omit the accessToken or
replace it with a masked value (e.g., "****") so logs never contain the raw
token.
| @NotBlank(message = "Access token is required") | ||
| @JsonProperty("accessToken") | ||
| private String accessToken; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Protect sensitive credential from accidental exposure.
The accessToken field contains sensitive credentials that could be accidentally logged or exposed through error messages, debug output, or toString() implementations. Consider adding @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) to prevent the token from being serialized in responses.
🔎 Proposed fix
@NotBlank(message = "Access token is required")
- @JsonProperty("accessToken")
+ @JsonProperty(value = "accessToken", access = JsonProperty.Access.WRITE_ONLY)
private String accessToken;🤖 Prompt for AI Agents
In
java-ecosystem/services/web-server/src/main/java/org/rostilos/codecrow/webserver/vcs/dto/request/gitlab/GitLabRepositoryTokenRequest.java
around lines 12-14, the accessToken field is writable but currently can be
serialized and accidentally exposed; annotate the field with
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) to prevent it from being
serialized in responses, and also ensure it is excluded from any
toString/equals/hashCode outputs (e.g., add appropriate exclusions if using
Lombok) so the token cannot appear in logs or object dumps.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.