From 588855dc825ae24c6c8eb19c7efe6a91a3d6dd72 Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:12:05 +0400 Subject: [PATCH 1/9] fix(summary): pydantic model parsing issue --- src/reviewbot/agent/workflow/gitlab_notes.py | 2 +- src/reviewbot/core/reviews/review_model.py | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/reviewbot/agent/workflow/gitlab_notes.py b/src/reviewbot/agent/workflow/gitlab_notes.py index 262f6d2..5dc2079 100644 --- a/src/reviewbot/agent/workflow/gitlab_notes.py +++ b/src/reviewbot/agent/workflow/gitlab_notes.py @@ -297,7 +297,7 @@ def update_review_summary( ido_agent = create_ido_agent(model=model, tools=tools or []) llm_summary = ido_agent.with_structured_output(ReviewSummary).invoke(messages) - llm_summary = str(llm_summary) + llm_summary = llm_summary.summary if llm_summary else "Review completed successfully." except Exception as e: console.print(f"[yellow]Failed to generate LLM summary: {e}[/yellow]") diff --git a/src/reviewbot/core/reviews/review_model.py b/src/reviewbot/core/reviews/review_model.py index 5cbc44a..015c238 100644 --- a/src/reviewbot/core/reviews/review_model.py +++ b/src/reviewbot/core/reviews/review_model.py @@ -1,9 +1,5 @@ -from pydantic import RootModel +from pydantic import BaseModel -class ReviewSummary(RootModel[str]): - def __str__(self) -> str: - return self.root - - def __len__(self) -> int: - return len(self.root) +class ReviewSummary(BaseModel): + summary: str From 3a498f268851c7dec28a28e5629443d82d298977 Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 14:19:50 +0400 Subject: [PATCH 2/9] fix: use with tool caller --- src/reviewbot/agent/workflow/gitlab_notes.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/reviewbot/agent/workflow/gitlab_notes.py b/src/reviewbot/agent/workflow/gitlab_notes.py index 5dc2079..71c0762 100644 --- a/src/reviewbot/agent/workflow/gitlab_notes.py +++ b/src/reviewbot/agent/workflow/gitlab_notes.py @@ -1,6 +1,7 @@ from typing import Any, NamedTuple from urllib.parse import quote +from ido_agents.agents.tool_runner import ToolCallerSettings from langchain_core.language_models.chat_models import BaseChatModel from langgraph.func import task # type: ignore from rich.console import Console # type: ignore @@ -147,7 +148,6 @@ def post_review_acknowledgment( try: # Get response with no tool calls allowed from ido_agents.agents.ido_agent import create_ido_agent - from ido_agents.agents.tool_runner import ToolCallerSettings summary_settings = ToolCallerSettings(max_tool_calls=0) ido_agent = create_ido_agent(model=model, tools=[]) @@ -295,7 +295,13 @@ def update_review_summary( raise ValueError("model parameter is required for ido-agents migration") ido_agent = create_ido_agent(model=model, tools=tools or []) - llm_summary = ido_agent.with_structured_output(ReviewSummary).invoke(messages) + + summary_settings = ToolCallerSettings(max_tool_calls=0) + llm_summary = ( + ido_agent.with_structured_output(ReviewSummary) + .with_tool_caller(summary_settings) + .invoke(messages) + ) llm_summary = llm_summary.summary if llm_summary else "Review completed successfully." From 769877e73e1297ce7a3100f37a4705cda6ac058b Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:42:02 +0400 Subject: [PATCH 3/9] refactor: extract read_file logic to a func --- src/reviewbot/agent/tasks/issues.py | 52 ++++---------- src/reviewbot/agent/workflow/gitlab_notes.py | 11 +-- src/reviewbot/tools/read_file.py | 72 ++++++++++++-------- 3 files changed, 65 insertions(+), 70 deletions(-) diff --git a/src/reviewbot/agent/tasks/issues.py b/src/reviewbot/agent/tasks/issues.py index 26e6b2c..6975b46 100644 --- a/src/reviewbot/agent/tasks/issues.py +++ b/src/reviewbot/agent/tasks/issues.py @@ -14,6 +14,7 @@ from reviewbot.core.issues import IssueModel from reviewbot.core.issues.issue_model import IssueModelList from reviewbot.tools.diff import get_diff_from_file +from reviewbot.tools.read_file import read_file_from_store console = Console() @@ -371,6 +372,7 @@ async def quick_scan_file( try: diff_content = get_diff_from_file(agent.store, file_path) + file_content = read_file_from_store(agent.store, file_path) except Exception as e: console.print(f"[yellow]Could not fetch diff for {file_path}: {e}[/yellow]") return True # If can't get diff, do deep review to be safe @@ -399,11 +401,13 @@ async def quick_scan_file( HumanMessage( content=f"""Quickly scan this file and determine if it needs deep review: {file_path} -Here is the diff: -```diff +Here is the file: +{file_content} + +Here is the diff: {diff_content} -```""" +""" ), ] @@ -482,32 +486,10 @@ async def review_single_file( reasoning_context = get_reasoning_context(agent.store) # Force a reasoning pass to ensure think() is invoked during deep review - try: - diff_content = get_diff_from_file(agent.store, file_path) - think_messages: list[BaseMessage] = [ - SystemMessage( - content=( - "You are a senior code reviewer. You must think and review. " - "with 2-5 sentences of reasoning about the provided diff. " - "Do not use any other tools. Once finished, reply with the " - "single word DONE." - ) - ), - HumanMessage( - content=f"""Diff for {file_path}: - -```diff -{diff_content} -``` -""", - ), - ] - if model: - think_settings = ToolCallerSettings(max_tool_calls=40) - ido_agent = create_ido_agent(model=model, tools=tools or []) - await ido_agent.with_tool_caller(think_settings).ainvoke(think_messages) - except Exception as e: - console.print(f"[yellow]Failed to record reasoning for {file_path}: {e}[/yellow]") + diff_content = get_diff_from_file(agent.store, file_path) + file_content = read_file_from_store(agent.store, file_path) + if not file_content: + file_content = "" messages: list[BaseMessage] = [ SystemMessage( @@ -584,15 +566,11 @@ async def review_single_file( HumanMessage( content=f"""Review the merge request diff for the file: {file_path} -WORKFLOW: -1. Use `get_diff("{file_path}")` to get the diff -2. Analyze the changes for bugs, security issues, and logic errors -3. If you need context beyond the diff (imports, variable declarations, surrounding code): - - Use `read_file("{file_path}")` to see the complete file -4. Use `think()` to document your reasoning and analysis -5. Return your findings as a list of issues (or empty list if none) +File content: +{file_content} -Find the real bugs - that's what matters most! +Diff: +{diff_content} """ ), ] diff --git a/src/reviewbot/agent/workflow/gitlab_notes.py b/src/reviewbot/agent/workflow/gitlab_notes.py index 71c0762..06abf1c 100644 --- a/src/reviewbot/agent/workflow/gitlab_notes.py +++ b/src/reviewbot/agent/workflow/gitlab_notes.py @@ -29,7 +29,7 @@ class AcknowledgmentResult(NamedTuple): @task -def post_review_acknowledgment( +async def post_review_acknowledgment( *, gitlab: GitProviderConfig, diffs: list[FileDiff], model: BaseChatModel ) -> AcknowledgmentResult | None: """ @@ -151,7 +151,7 @@ def post_review_acknowledgment( summary_settings = ToolCallerSettings(max_tool_calls=0) ido_agent = create_ido_agent(model=model, tools=[]) - summary = ido_agent.with_tool_caller(summary_settings).invoke(messages) + summary = await ido_agent.with_tool_caller(summary_settings).ainvoke(messages) # Post as a discussion (so we can update it later) acknowledgment_body = f""" @@ -186,7 +186,8 @@ def post_review_acknowledgment( return None -def update_review_summary( +@task +async def update_review_summary( api_v4: str, token: str, project_id: str, @@ -297,10 +298,10 @@ def update_review_summary( ido_agent = create_ido_agent(model=model, tools=tools or []) summary_settings = ToolCallerSettings(max_tool_calls=0) - llm_summary = ( + llm_summary = await ( ido_agent.with_structured_output(ReviewSummary) .with_tool_caller(summary_settings) - .invoke(messages) + .ainvoke(messages) ) llm_summary = llm_summary.summary if llm_summary else "Review completed successfully." diff --git a/src/reviewbot/tools/read_file.py b/src/reviewbot/tools/read_file.py index 46b4174..26a8e87 100644 --- a/src/reviewbot/tools/read_file.py +++ b/src/reviewbot/tools/read_file.py @@ -1,6 +1,7 @@ from pathlib import Path from langchain.tools import ToolRuntime, tool # type: ignore +from langgraph.func import BaseStore # type: ignore from rich.console import Console from reviewbot.agent.workflow.state import CodebaseState @@ -8,43 +9,19 @@ console = Console() -@tool -def read_file( +def read_file_from_store( + store: BaseStore, file_path: str, - runtime: ToolRuntime, # type: ignore line_start: int | None = None, line_end: int | None = None, ) -> str: - """Read the contents of a file from the repository. - - Use this tool to get the full context of a file when the diff alone is not sufficient - to understand the code. This helps avoid false positives by seeing the complete picture. - - Args: - file_path: Relative path to the file in the repository (e.g., "src/main.go") - line_start: Optional line number to start reading from (1-indexed) - line_end: Optional line number to stop reading at (inclusive) - - Returns: - The file contents, optionally limited to the specified line range - - Examples: - - read_file("src/main.go") - Read entire file - - read_file("src/main.go", line_start=10, line_end=50) - Read lines 10-50 - - Note: - Returns an error message if the file is newly added (doesn't exist in current checkout) - """ - if runtime.store is None: - console.print("[red]read_file: Store not found in runtime[/red]") - raise ValueError("Store not found in runtime") - + """Read file contents using the codebase state stored in the runtime store.""" line_range = f" (lines {line_start}-{line_end})" if line_start or line_end else "" console.print(f"[cyan]read_file: '{file_path}'{line_range}[/cyan]") # Get codebase state from store NS = ("codebase",) - raw = runtime.store.get(NS, "state") + raw = store.get(NS, "state") if not raw: console.print("[red]read_file: Codebase state not found in store[/red]") raise ValueError("Codebase state not found in store") @@ -174,3 +151,42 @@ def read_file( console.print(f"[dim] → Message: {error_msg}[/dim]") raise ValueError(error_msg) from e + + +@tool +def read_file( + file_path: str, + runtime: ToolRuntime, # type: ignore + line_start: int | None = None, + line_end: int | None = None, +) -> str: + """Read the contents of a file from the repository. + + Use this tool to get the full context of a file when the diff alone is not sufficient + to understand the code. This helps avoid false positives by seeing the complete picture. + + Args: + file_path: Relative path to the file in the repository (e.g., "src/main.go") + line_start: Optional line number to start reading from (1-indexed) + line_end: Optional line number to stop reading at (inclusive) + + Returns: + The file contents, optionally limited to the specified line range + + Examples: + - read_file("src/main.go") - Read entire file + - read_file("src/main.go", line_start=10, line_end=50) - Read lines 10-50 + + Note: + Returns an error message if the file is newly added (doesn't exist in current checkout) + """ + if runtime.store is None: + console.print("[red]read_file: Store not found in runtime[/red]") + raise ValueError("Store not found in runtime") + + return read_file_from_store( + runtime.store, + file_path, + line_start=line_start, + line_end=line_end, + ) From f1d52a3f7910591a3c12ba5bfd1c3684f265356e Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 15:55:10 +0400 Subject: [PATCH 4/9] feat: contextful summaries even when there is no issues --- src/reviewbot/agent/workflow/gitlab_notes.py | 51 ++++++++++++++++++-- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/reviewbot/agent/workflow/gitlab_notes.py b/src/reviewbot/agent/workflow/gitlab_notes.py index 06abf1c..2bf7bcc 100644 --- a/src/reviewbot/agent/workflow/gitlab_notes.py +++ b/src/reviewbot/agent/workflow/gitlab_notes.py @@ -235,6 +235,43 @@ async def update_review_summary( total_files = len(diffs) files_with_issues = len(issues_by_file) + # Build change overview for context + new_files: list[str] = [] + deleted_files: list[str] = [] + renamed_files: list[tuple[str, str]] = [] + modified_files: list[str] = [] + + for diff in diffs: + if diff.is_renamed: + renamed_files.append((diff.old_path or "unknown", diff.new_path or "unknown")) + elif diff.is_new_file: + new_files.append(diff.new_path or diff.old_path or "unknown") + elif diff.is_deleted_file: + deleted_files.append(diff.old_path or diff.new_path or "unknown") + else: + modified_files.append(diff.new_path or diff.old_path or "unknown") + + change_stats = ( + "Files changed: " + f"{total_files} (new: {len(new_files)}, modified: {len(modified_files)}, " + f"renamed: {len(renamed_files)}, deleted: {len(deleted_files)})" + ) + change_overview_lines: list[str] = [] + change_overview_lines.extend(f"- {path} (new)" for path in new_files) + change_overview_lines.extend(f"- {path} (deleted)" for path in deleted_files) + change_overview_lines.extend(f"- {old} -> {new} (renamed)" for old, new in renamed_files) + change_overview_lines.extend(f"- {path} (modified)" for path in modified_files) + + max_change_lines = 12 + if len(change_overview_lines) > max_change_lines: + remaining = len(change_overview_lines) - max_change_lines + change_overview_lines = change_overview_lines[:max_change_lines] + change_overview_lines.append(f"- ... and {remaining} more file(s)") + + change_overview_text = ( + "\n".join(change_overview_lines) if change_overview_lines else "- No files listed." + ) + # Prepare issue details for LLM issues_summary: list[str] = [] for issue in issues: @@ -257,7 +294,7 @@ async def update_review_summary( - Provide reasoning about the overall merge request purpose and code quality. - Highlight key concerns or positive aspects - Be constructive and professional -- DO NOT use any tools +- Use tools to generate a comprehensive summary - Use paragraphs with readable flow. Use two paragrahs with 3-5 sentences. Paragraphs should be wrapped with

tags. Use new

tag for a newline. Example @@ -268,7 +305,8 @@ async def update_review_summary(

paragraph2

-- Focus on the big picture, not individual issue details""" +- Focus on the big picture, not individual issue details +- Reference the changes overview so the summary stays grounded in what changed, even if there are no issues""" ), HumanMessage( content=f"""A code review has been completed with the following results: @@ -281,6 +319,10 @@ async def update_review_summary( - Medium severity: {medium_count} - Low severity: {low_count} +**Changes overview:** +{change_stats} +{change_overview_text} + **Issues found:** {issues_text} @@ -288,7 +330,8 @@ async def update_review_summary( 1. Provides overall assessment of the purpose of the merge request purpose and code quality. 2. Highlights the most important concerns (if any) 3. Gives reasoning about the review findings -4. Is constructive and actionable """ +4. Is constructive and actionable +5. Mention the kinds of changes and at least one example file from the changes overview """ ), ] @@ -297,7 +340,7 @@ async def update_review_summary( ido_agent = create_ido_agent(model=model, tools=tools or []) - summary_settings = ToolCallerSettings(max_tool_calls=0) + summary_settings = ToolCallerSettings(max_tool_calls=5) llm_summary = await ( ido_agent.with_structured_output(ReviewSummary) .with_tool_caller(summary_settings) From ff1d670f6941342b01901dfb6a630560d650faa2 Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:05:18 +0400 Subject: [PATCH 5/9] chore: remove unused code --- src/reviewbot/agent/workflow/diff_extract.py | 40 -------------------- 1 file changed, 40 deletions(-) diff --git a/src/reviewbot/agent/workflow/diff_extract.py b/src/reviewbot/agent/workflow/diff_extract.py index 73a76d7..18c9b40 100644 --- a/src/reviewbot/agent/workflow/diff_extract.py +++ b/src/reviewbot/agent/workflow/diff_extract.py @@ -3,46 +3,6 @@ from typing import Any -def _extract_code_from_diff(diff_text: str, line_start: int, line_end: int) -> str: - hunk_header_pattern = re.compile(r"^@@\s+-(\d+)(?:,(\d+))?\s+\+(\d+)(?:,(\d+))?\s+@@") - lines = diff_text.splitlines() - - extracted = [] - current_new = 0 - in_hunk = False - - for line in lines: - match = hunk_header_pattern.match(line) - if match: - current_new = int(match.group(3)) - in_hunk = True - continue - - if not in_hunk: - continue - - # We only care about the lines in the NEW file (the result of the change) - if line.startswith("+"): - if line_start <= current_new <= line_end: - extracted.append(line[1:]) # Remove '+' - current_new += 1 - elif line.startswith("-"): - # Skip deleted lines for code extraction of the 'new' state - continue - else: - # Context line - if line_start <= current_new <= line_end: - extracted.append(line[1:] if line else "") - current_new += 1 - - # FIX: Exit early if we've passed the end of our requested range - if current_new > line_end: - if extracted: # Only break if we actually found lines - break - - return "\n".join(extracted) - - def generate_line_code(file_path: str, old_line: int | None, new_line: int | None) -> str: """ Generates a GitLab-compatible line_code. From 36d4701c02314fc933c47256bc4a21e1060b3ccc Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:05:36 +0400 Subject: [PATCH 6/9] refactor: update validator to filter out false compile errors --- src/reviewbot/agent/tasks/issues.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/reviewbot/agent/tasks/issues.py b/src/reviewbot/agent/tasks/issues.py index 6975b46..d7b4102 100644 --- a/src/reviewbot/agent/tasks/issues.py +++ b/src/reviewbot/agent/tasks/issues.py @@ -640,6 +640,7 @@ async def validate_issues_for_file( SystemMessage( content=( "You are an issue validator. Your job is to remove FALSE POSITIVES while keeping real bugs.\n\n" + "The codebase already has been linted, built, formatted and compiled successfully. Make sure to remove 'issues' that claim otherwise." "AVAILABLE TOOLS:\n" "- `read_file(file_path)` - Read the complete file to verify issues\n" "- `ls_dir(dir_path)` - List directory contents to verify file structure\n\n" @@ -647,6 +648,7 @@ async def validate_issues_for_file( "- 'Variable X undefined' - when X is actually defined elsewhere in the file\n" "- 'Import Y missing' - when Y exists at the top of the file\n" "- 'Function Z not declared' - when Z is defined in the complete file\n\n" + "- 'Compile error' - the codebase is already compiled successfully.\n" "WHAT TO KEEP (real issues):\n" "- Logic errors - wrong conditions, broken algorithms, incorrect business logic\n" "- Security vulnerabilities - SQL injection, XSS, auth bypass, etc.\n" @@ -683,7 +685,7 @@ async def validate_issues_for_file( ), ] - validation_settings = ToolCallerSettings(max_tool_calls=5) # Allow tool calls for validation + validation_settings = ToolCallerSettings(max_tool_calls=10) # Allow tool calls for validation try: if model is None: From d8d2026c600b0d28fc6de4ed36b3bea9b9ed70f6 Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:09:29 +0400 Subject: [PATCH 7/9] refactor: tighten validation prompt --- src/reviewbot/agent/tasks/issues.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/reviewbot/agent/tasks/issues.py b/src/reviewbot/agent/tasks/issues.py index d7b4102..727e3a6 100644 --- a/src/reviewbot/agent/tasks/issues.py +++ b/src/reviewbot/agent/tasks/issues.py @@ -640,7 +640,8 @@ async def validate_issues_for_file( SystemMessage( content=( "You are an issue validator. Your job is to remove FALSE POSITIVES while keeping real bugs.\n\n" - "The codebase already has been linted, built, formatted and compiled successfully. Make sure to remove 'issues' that claim otherwise." + "The codebase already has been linted, built, formatted and compiled successfully. " + "Make sure to remove 'issues' that claim otherwise.\n\n" "AVAILABLE TOOLS:\n" "- `read_file(file_path)` - Read the complete file to verify issues\n" "- `ls_dir(dir_path)` - List directory contents to verify file structure\n\n" @@ -648,6 +649,9 @@ async def validate_issues_for_file( "- 'Variable X undefined' - when X is actually defined elsewhere in the file\n" "- 'Import Y missing' - when Y exists at the top of the file\n" "- 'Function Z not declared' - when Z is defined in the complete file\n\n" + "- Speculation about other files or the wider codebase that is NOT shown in the diff\n" + "- Claims that a deletion breaks other code unless the breakage is proven in this file's diff\n" + "- Mentions of symbols/types/functions that do not appear in the diff or file content\n" "- 'Compile error' - the codebase is already compiled successfully.\n" "WHAT TO KEEP (real issues):\n" "- Logic errors - wrong conditions, broken algorithms, incorrect business logic\n" @@ -658,6 +662,8 @@ async def validate_issues_for_file( "RULES:\n" "- KEEP issues about logic, bugs, security, and functionality\n" "- ONLY remove issues that are provably false (use read_file to verify)\n" + "- Do NOT infer cross-file breakages; validate only what the diff/file proves\n" + "- If a file is deleted and the issue is about missing types/functions elsewhere, remove it\n" "- When in doubt, KEEP the issue - don't filter out real bugs\n" "- Do NOT create new issues\n" "- Do NOT modify issue fields" @@ -678,6 +684,7 @@ async def validate_issues_for_file( 1. For issues about "undefined/missing" code, use `read_file("{file_path}")` to check if the code actually exists elsewhere 2. Remove ONLY clear false positives 3. Keep all logic bugs, security issues, and real functionality problems +4. Discard any issue that relies on assumptions about other files not shown in this diff/file Return a ValidationResult with: - valid_issues: confirmed real issues From bc01f381de9af5caf9b5bc6812c5e8409f2c453e Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:20:05 +0400 Subject: [PATCH 8/9] refactor: removed line in prompt that caused false positives --- src/reviewbot/agent/tasks/issues.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/reviewbot/agent/tasks/issues.py b/src/reviewbot/agent/tasks/issues.py index 727e3a6..d7ec72c 100644 --- a/src/reviewbot/agent/tasks/issues.py +++ b/src/reviewbot/agent/tasks/issues.py @@ -664,7 +664,7 @@ async def validate_issues_for_file( "- ONLY remove issues that are provably false (use read_file to verify)\n" "- Do NOT infer cross-file breakages; validate only what the diff/file proves\n" "- If a file is deleted and the issue is about missing types/functions elsewhere, remove it\n" - "- When in doubt, KEEP the issue - don't filter out real bugs\n" + "- Remove any issue claiming compile/runtime breakage unless the diff/file itself shows the failure\n" "- Do NOT create new issues\n" "- Do NOT modify issue fields" ) @@ -685,6 +685,7 @@ async def validate_issues_for_file( 2. Remove ONLY clear false positives 3. Keep all logic bugs, security issues, and real functionality problems 4. Discard any issue that relies on assumptions about other files not shown in this diff/file +5. Keep an issue only if you can point to concrete evidence in the diff/file (line numbers) that supports it Return a ValidationResult with: - valid_issues: confirmed real issues From e9eddf7de58023e193a16a5be27748b08dbacdf6 Mon Sep 17 00:00:00 2001 From: canefe <8518141+canefe@users.noreply.github.com> Date: Thu, 15 Jan 2026 16:27:50 +0400 Subject: [PATCH 9/9] refactor: further improve validation --- src/reviewbot/agent/tasks/issues.py | 49 ++++++++++------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/reviewbot/agent/tasks/issues.py b/src/reviewbot/agent/tasks/issues.py index d7ec72c..1638b55 100644 --- a/src/reviewbot/agent/tasks/issues.py +++ b/src/reviewbot/agent/tasks/issues.py @@ -639,34 +639,19 @@ async def validate_issues_for_file( messages: list[BaseMessage] = [ SystemMessage( content=( - "You are an issue validator. Your job is to remove FALSE POSITIVES while keeping real bugs.\n\n" - "The codebase already has been linted, built, formatted and compiled successfully. " - "Make sure to remove 'issues' that claim otherwise.\n\n" - "AVAILABLE TOOLS:\n" - "- `read_file(file_path)` - Read the complete file to verify issues\n" - "- `ls_dir(dir_path)` - List directory contents to verify file structure\n\n" - "WHAT TO REMOVE (false positives):\n" - "- 'Variable X undefined' - when X is actually defined elsewhere in the file\n" - "- 'Import Y missing' - when Y exists at the top of the file\n" - "- 'Function Z not declared' - when Z is defined in the complete file\n\n" - "- Speculation about other files or the wider codebase that is NOT shown in the diff\n" - "- Claims that a deletion breaks other code unless the breakage is proven in this file's diff\n" - "- Mentions of symbols/types/functions that do not appear in the diff or file content\n" - "- 'Compile error' - the codebase is already compiled successfully.\n" - "WHAT TO KEEP (real issues):\n" - "- Logic errors - wrong conditions, broken algorithms, incorrect business logic\n" - "- Security vulnerabilities - SQL injection, XSS, auth bypass, etc.\n" - "- Bugs that will crash or produce wrong results\n" - "- Data corruption risks\n" - "- Performance problems\n\n" - "RULES:\n" - "- KEEP issues about logic, bugs, security, and functionality\n" - "- ONLY remove issues that are provably false (use read_file to verify)\n" - "- Do NOT infer cross-file breakages; validate only what the diff/file proves\n" - "- If a file is deleted and the issue is about missing types/functions elsewhere, remove it\n" - "- Remove any issue claiming compile/runtime breakage unless the diff/file itself shows the failure\n" - "- Do NOT create new issues\n" - "- Do NOT modify issue fields" + "You are a strict Issue Validator filter. The project is ALREADY COMPILED AND RUNNING.\n\n" + "### CRITICAL CONTEXT\n" + "1. THE CODEBASE HAS ALREADY COMPILED AND BUILT SUCCESSFULLY.\n" + "2. If a file is deleted in a diff, it means the references were already cleaned up.\n" + "3. PROVISION: Any issue claiming 'compilation error', 'missing reference', or 'broken startup' is FACTUALLY WRONG.\n\n" + "### VALIDATION ARCHITECTURE\n" + "Your ONLY goal is to discard issues that assume the code is currently broken. " + "Since the build passed, the code is structurally sound. You are only looking for LOGIC bugs in NEW code.\n\n" + "### DISCARD IMMEDIATELY (False Positives):\n" + "- **Deletions:** Claims that deleting code/files will break the app (The build passed, so it didn't).\n" + "- **References:** Claims that a symbol is undefined (It is likely defined in a file you can't see).\n" + "- **Build/Runtime:** Any mention of 'compilation errors', 'build failures', or 'initialization failures'.\n" + "- **Assumptions:** Speculation about files outside the provided diff.\n\n" ) ), HumanMessage( @@ -681,11 +666,9 @@ async def validate_issues_for_file( {json.dumps(issues_payload, indent=2)} TASK: -1. For issues about "undefined/missing" code, use `read_file("{file_path}")` to check if the code actually exists elsewhere -2. Remove ONLY clear false positives -3. Keep all logic bugs, security issues, and real functionality problems -4. Discard any issue that relies on assumptions about other files not shown in this diff/file -5. Keep an issue only if you can point to concrete evidence in the diff/file (line numbers) that supports it +1. If the diff shows a file being DELETED, and the issue claims this deletion causes a failure elsewhere: DISCARD THE ISSUE. +2. The fact that the file was deleted and the project STILL COMPILED means the initialization logic was moved or is no longer necessary. +3. Validate only the logic within the lines starting with '+' (added). Return a ValidationResult with: - valid_issues: confirmed real issues