Skip to content

Add review risk assessment and optional repository review rules#2391

Open
xiaodeng666a wants to merge 25 commits into
The-PR-Agent:mainfrom
xiaodeng666a:feat/rules-and-review-history
Open

Add review risk assessment and optional repository review rules#2391
xiaodeng666a wants to merge 25 commits into
The-PR-Agent:mainfrom
xiaodeng666a:feat/rules-and-review-history

Conversation

@xiaodeng666a
Copy link
Copy Markdown

Summary

This PR introduces two related review enhancements for PR-Agent:

  1. Structured review assessment
  • add risk_level
  • add merge_recommendation
  • add review_priority_files
  1. Optional repository review rules
  • allow repositories to provide review rules through configurable markdown files
  • inject repository/team-specific guidance into the review prompt
  • keep behavior unchanged when no rules file exists

Motivation

These changes make review output more actionable and more adaptable to repository-specific expectations, while keeping the default behavior backward-compatible.

Notes

  • repository review rules are optional
  • no personal model configuration is included in this PR
  • local review history is intentionally excluded and will be proposed separately if needed

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add review risk assessment, merge recommendations, and repository review rules

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add risk assessment and merge recommendation fields to PR reviews
• Implement optional repository-specific review rules injection
• Add review history tracking and retrieval from JSONL file
• Enhance markdown rendering for new review fields
Diagram
flowchart LR
  A["PR Review Process"] --> B["Load Review Rules"]
  A --> C["Load Review History"]
  B --> D["Inject into Prompt"]
  C --> D
  D --> E["Generate Review"]
  E --> F["Extract Risk Level"]
  E --> G["Extract Merge Recommendation"]
  E --> H["Extract Priority Files"]
  F --> I["Store in Memory"]
  G --> I
  H --> I
  I --> J["Render Markdown"]
Loading

Grey Divider

File Changes

1. pr_agent/algo/utils.py ✨ Enhancement +32/-0

Add markdown rendering for risk assessment fields

• Add markdown rendering for risk_level field with capitalization
• Add markdown rendering for merge_recommendation field
• Add markdown rendering for review_priority_files list with proper formatting
• Support both GFM table format and standard markdown format

pr_agent/algo/utils.py


2. pr_agent/tools/pr_reviewer.py ✨ Enhancement +177/-4

Implement review rules and history management system

• Add _get_review_rules() method to load repository review rules from configurable paths
• Add _get_review_history() method to load and filter past reviews from JSONL file
• Add _store_review_memory() method to persist review data for future reference
• Add _get_review_memory_file() and _get_changed_filenames() helper methods
• Integrate review rules and history into prompt context variables
• Add new configuration flags for risk assessment, merge recommendation, and priority files
• Store review memory after generating review output

pr_agent/tools/pr_reviewer.py


3. pr_agent/settings/configuration.toml ⚙️ Configuration changes +6/-0

Add configuration for risk assessment and review rules

• Add require_risk_assessment=true configuration flag
• Add require_merge_recommendation=true configuration flag
• Add require_priority_files=true configuration flag
• Add enable_review_rules=true configuration flag
• Add review_rules_paths with default paths for review rule files
• Add max_review_rules_tokens=1200 to limit review rules token usage

pr_agent/settings/configuration.toml


View more (1)
4. pr_agent/settings/pr_reviewer_prompts.toml ✨ Enhancement +56/-0

Extend prompt template with rules and history sections

• Add conditional section for injecting repository review rules into prompt
• Add conditional section for injecting review history into prompt
• Add instructions for prioritizing repository rules over generic preferences
• Add instructions for using review history as supporting context only
• Add new YAML schema fields for risk_level, merge_recommendation, and review_priority_files
• Add example values for new review assessment fields in output examples

pr_agent/settings/pr_reviewer_prompts.toml


Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 14, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (3)

Grey Divider


Action required

1. Gitea rules never load ✓ Resolved 🐞 Bug ≡ Correctness
Description
On Gitea, PRReviewer._get_review_rules() attempts to fetch rule files via
git_provider.get_pr_file_content(), but GiteaProvider does not implement that method, so rule
loading always fails and silently results in no injected rules. This makes the review-rules feature
non-functional for Gitea repositories.
Code

pr_agent/git_providers/gitea_provider.py[R581-582]

+    def get_pr_base_ref(self) -> str:
+        return self.base_sha or self.base_ref or ""
Evidence
PRReviewer always loads review rules by calling get_pr_file_content() on the active provider, but
GitProvider does not define this method and GiteaProvider only exposes internal file-content
helpers, so the call cannot succeed on Gitea and rules will never be injected.

pr_agent/tools/pr_reviewer.py[128-183]
pr_agent/git_providers/git_provider.py[176-195]
pr_agent/git_providers/gitea_provider.py[428-442]
pr_agent/git_providers/gitea_provider.py[569-583]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
PRReviewer._get_review_rules() relies on `git_provider.get_pr_file_content(file_path, ref)` to load repository review rules, but `GiteaProvider` does not implement this method. As a result, review rules never load on Gitea (calls raise AttributeError and are swallowed), so `review_rules` is always empty.
## Issue Context
- `_get_review_rules()` is invoked during `PRReviewer.__init__` when `enable_review_rules` is enabled.
- Gitea already has the necessary primitives (`repo_api.get_file_content`, `_get_file_content_from_base`) to implement `get_pr_file_content`.
## Fix Focus Areas
- pr_agent/git_providers/gitea_provider.py[569-583]
- pr_agent/tools/pr_reviewer.py[148-183]
- pr_agent/git_providers/git_provider.py[176-195]
## Expected fix
- Add `def get_pr_file_content(self, file_path: str, branch: str) -> str:` to `GiteaProvider`, using `self.repo_api.get_file_content(owner=self.owner, repo=self.repo, commit_sha=branch, filepath=file_path)` (where `branch` may be a branch name or commit SHA).
- Handle missing files by returning an empty string, consistent with other providers.
- (Optional but recommended) Add `get_pr_file_content` to the `GitProvider` interface (abstract or default) to make this contract explicit.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Bitbucket Server rules skipped ✓ Resolved 🐞 Bug ≡ Correctness
Description
PRReviewer._get_review_rules() calls git_provider.get_pr_file_content(), but BitbucketServerProvider
does not implement that method (it only has a stub _get_pr_file_content()), so review rules will
never load on Bitbucket Server even when the rules file exists.
Code

pr_agent/tools/pr_reviewer.py[R155-159]

+        for rule_path in rule_paths:
+            try:
+                rule_content = self.git_provider.get_pr_file_content(rule_path, ref)
+            except Exception:
+                continue
Evidence
_get_review_rules() unconditionally calls get_pr_file_content(), but GitProvider doesn’t
define it as part of the interface and Bitbucket Server’s provider file shows only a stub
_get_pr_file_content() plus a separate get_file() helper, meaning the review rules fetch path
has no implementation on Bitbucket Server.

pr_agent/tools/pr_reviewer.py[128-167]
pr_agent/git_providers/git_provider.py[157-187]
pr_agent/git_providers/bitbucket_server_provider.py[191-210]
pr_agent/git_providers/bitbucket_server_provider.py[502-513]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PRReviewer._get_review_rules()` depends on `git_provider.get_pr_file_content(path, ref)` to read rule files at the PR base ref. `BitbucketServerProvider` does not implement `get_pr_file_content`, so the call raises and is swallowed, causing rules to never load for Bitbucket Server PRs.
### Issue Context
- `_get_review_rules()` is called during `PRReviewer.__init__`, so this affects every `/review` run when `enable_review_rules` is enabled.
- `BitbucketServerProvider` already has `get_file(path, commit_id)` which can fetch file contents given a commit id, and `get_pr_base_ref()` prefers returning `toRef.latestCommit` (a commit id).
### Fix Focus Areas
- pr_agent/git_providers/bitbucket_server_provider.py[201-210]
- pr_agent/git_providers/bitbucket_server_provider.py[502-513]
- pr_agent/tools/pr_reviewer.py[155-167]
### Suggested fix
- Add `def get_pr_file_content(self, file_path: str, branch: str) -> str:` to `BitbucketServerProvider` that calls `self.get_file(file_path, branch)` when `branch` is a commit id.
- Optionally, if `branch` can be a branch name, resolve it to a commit id (or document that `_get_review_rules()` should always pass a commit SHA for Bitbucket Server).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Review rules lack tests ✓ Resolved 📘 Rule violation ☼ Reliability
Description
This PR adds new /review behavior (repo review rules injection + new review fields rendering) but
does not add/update pytest coverage for these new outputs, increasing regression risk and making
changes harder to validate.
Code

pr_agent/tools/pr_reviewer.py[R128-183]

+    def _get_review_rules(self) -> str:
+        if not get_settings().pr_reviewer.get("enable_review_rules", False):
+            return ""
+
+        rule_paths = get_settings().pr_reviewer.get("review_rules_paths", []) or []
+        if isinstance(rule_paths, str):
+            rule_paths = [rule_paths]
+        elif isinstance(rule_paths, (list, tuple)):
+            rule_paths = [
+                str(rule_path).strip()
+                for rule_path in rule_paths
+                if str(rule_path).strip()
+            ]
+        else:
+            get_logger().warning(
+                "Invalid review_rules_paths value; expected string or list of strings",
+                artifacts={"review_rules_paths": rule_paths},
+            )
+            rule_paths = []
+
+        ref = self.git_provider.get_pr_base_ref()
+        if not ref:
+            get_logger().warning("Could not resolve a trusted base ref for review rules")
+            return ""
+        loaded_rules = []
+        loaded_rule_paths = []
+
+        for rule_path in rule_paths:
+            try:
+                rule_content = self.git_provider.get_pr_file_content(rule_path, ref)
+            except Exception:
+                continue
+
+            if rule_content and rule_content.strip():
+                loaded_rule_paths.append(rule_path)
+                loaded_rules.append(f"File: `{rule_path}`\n{rule_content.strip()}")
+
+        if not loaded_rules:
+            get_logger().info("No review rules file found for this PR")
+            return ""
+
+        review_rules = "\n\n---\n\n".join(loaded_rules)
+        max_tokens = get_settings().pr_reviewer.get("max_review_rules_tokens", 0)
+        try:
+            max_tokens = int(max_tokens)
+        except (TypeError, ValueError):
+            get_logger().warning(
+                "Invalid max_review_rules_tokens value; skipping token clipping",
+                artifacts={"max_review_rules_tokens": max_tokens},
+            )
+            max_tokens = 0
+
+        if max_tokens > 0:
+            review_rules = clip_tokens(review_rules, max_tokens)
+        get_logger().info("Loaded review rules for this PR", artifacts={"rule_files": loaded_rule_paths})
+        return review_rules
Evidence
The checklist requires behavior changes to be covered by pytest tests in the appropriate suite. The
PR introduces new review prompt schema fields and new markdown rendering branches, but existing
markdown tests do not cover the new keys and there are no tests covering the new review-rules
loading path.

AGENTS.md
pr_agent/tools/pr_reviewer.py[128-183]
pr_agent/algo/utils.py[241-270]
pr_agent/settings/pr_reviewer_prompts.toml[71-80]
pr_agent/settings/pr_reviewer_prompts.toml[122-137]
tests/unittest/test_convert_to_markdown.py[48-125]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New review features were added (review-rules loading/injection and new `risk_level`/`merge_recommendation`/`review_priority_files` formatting), but there are no accompanying pytest updates covering the new behavior.
## Issue Context
The repository already has unit tests for markdown conversion, but none exercise the newly added keys or the review-rules loading logic; this can allow prompt/output regressions to slip through CI.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[128-183]
- pr_agent/algo/utils.py[241-270]
- pr_agent/settings/pr_reviewer_prompts.toml[71-80]
- pr_agent/settings/pr_reviewer_prompts.toml[122-137]
- tests/unittest/test_convert_to_markdown.py[48-125]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (15)
4. .pr_agent.toml missing new keys 📘 Rule violation ⚙ Maintainability
Description
New /review configuration knobs were added to pr_agent/settings/configuration.toml, but the
mirrored root .pr_agent.toml was not updated to include these options, risking divergence and user
confusion about available/default behavior. This violates the mirror-sync requirement when
behavior/config surface area changes.
Code

pr_agent/settings/configuration.toml[R84-89]

+require_risk_assessment=false
+require_merge_recommendation=false
+require_priority_files=false
+enable_review_rules=true
+review_rules_paths=[".pr_agent/review_rules.md",".github/review_rules.md","docs/review_rules.md"]
+max_review_rules_tokens=1200
Evidence
The PR introduces new pr_reviewer configuration entries in configuration.toml, but
.pr_agent.toml does not include corresponding keys under [pr_reviewer], leaving the mirror out
of sync.

AGENTS.md
pr_agent/settings/configuration.toml[84-89]
.pr_agent.toml[1-4]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New `pr_reviewer` settings were added in `pr_agent/settings/configuration.toml` but are not reflected in the root `.pr_agent.toml` mirror, making mirrored configuration sources inconsistent.
## Issue Context
The repo uses both a packaged default config (`pr_agent/settings/*.toml`) and a root `.pr_agent.toml` as a mirror/example for behavior changes.
## Fix Focus Areas
- pr_agent/settings/configuration.toml[84-89]
- .pr_agent.toml[1-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Bitbucket rules never load ✓ Resolved 🐞 Bug ≡ Correctness
Description
PRReviewer._get_review_rules() cannot resolve a trusted base ref on Bitbucket because it only checks
GitHub/GitLab-style attributes, so review rules are never loaded and the function returns "". With
enable_review_rules=true by default, Bitbucket runs will repeatedly log "Could not resolve a trusted
base ref" and silently skip the feature.
Code

pr_agent/tools/pr_reviewer.py[R148-158]

+        base = getattr(getattr(self.git_provider, "pr", None), "base", None)
+        ref = (
+            getattr(base, "sha", None)
+            or getattr(base, "ref", None)
+            or getattr(self.git_provider, "base_sha", None)
+            or getattr(self.git_provider, "base_ref", None)
+            or getattr(getattr(self.git_provider, "mr", None), "target_branch", None)
+        )
+        if not ref:
+            get_logger().warning("Could not resolve a trusted base ref for review rules")
+            return ""
Evidence
The ref-resolution logic in PRReviewer has no Bitbucket-specific fallback, while BitbucketProvider
exposes the target/base via destination_branch and does not define base_sha/base_ref, so
_get_review_rules() will fail to resolve ref on Bitbucket and return "".

pr_agent/tools/pr_reviewer.py[128-158]
pr_agent/git_providers/bitbucket_provider.py[27-75]
pr_agent/git_providers/bitbucket_provider.py[555-569]
pr_agent/settings/configuration.toml[74-90]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PRReviewer._get_review_rules()` tries to compute a "trusted base ref" using only GitHub/GitLab-style fields (`pr.base.*`, `base_sha/base_ref`, `mr.target_branch`). On Bitbucket, these attributes are not present, so `ref` resolves to `None`, the method warns, and returns an empty rules string, making review rules non-functional.
## Issue Context
- Bitbucket provider exposes base/destination information via `self.pr.destination_branch` and its `get_pr_file_content()` helper already knows how to translate destination/source branch names into commit hashes.
- The default configuration enables review rules (`enable_review_rules=true`), so this becomes a recurring runtime problem for Bitbucket users.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[128-190]
- pr_agent/git_providers/bitbucket_provider.py[555-569]
- pr_agent/settings/configuration.toml[74-90]
## Suggested fix
1. Extend the `ref = (...)` fallback chain to include a Bitbucket base ref, e.g.:
- `getattr(getattr(self.git_provider, "pr", None), "destination_branch", None)`
2. Optionally, avoid emitting a warning when review rules are enabled but the provider cannot supply a base ref (or downgrade to `info`), to prevent noisy logs.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. BOM character in import copy 📘 Rule violation ⚙ Maintainability
Description
pr_reviewer.py now begins with a hidden UTF-8 BOM/zero-width character before import copy, which
can cause linting/pre-commit failures, inconsistent behavior across linters/formatters, and
confusing diffs across editors/tools. This violates repository hygiene expectations for clean,
stable text formatting in changed files.
Code

pr_agent/tools/pr_reviewer.py[1]

+import copy
Evidence
PR Compliance ID 14 requires pre-commit hygiene (clean whitespace/formatting), and the first line of
pr_reviewer.py shows the import statement with a BOM prefix (visible in the diff as `+import
copy`), demonstrating that a non-printable character (UTF-8 BOM/U+FEFF) was introduced at the start
of the file.

AGENTS.md
pr_agent/tools/pr_reviewer.py[1-1]
pr_agent/tools/pr_reviewer.py[1-3]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`pr_agent/tools/pr_reviewer.py` has a hidden UTF-8 BOM/zero-width character (U+FEFF) at the very start of the file (before `import copy`). This non-printable character can break tooling, create inconsistent linter/formatter behavior, and violate pre-commit hygiene expectations.
## Issue Context
The diff shows the first line as `+import copy` (note the hidden character before `import`). This is typically introduced by an editor/encoding change and should be removed so the file starts directly with the import statement.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[1-1]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


7. New fields enabled by default ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
configuration.toml sets require_risk_assessment, require_merge_recommendation, and
require_priority_files to true, so the default review prompt and YAML output schema change for
every run that uses default settings. This increases prompt/output token usage and changes the
tool’s default review output even when callers didn’t opt into the new assessment fields.
Code

pr_agent/settings/configuration.toml[R84-86]

+require_risk_assessment=true
+require_merge_recommendation=true
+require_priority_files=true
Evidence
The default settings file enables the new fields, PRReviewer passes the flags into the Jinja prompt
variables, and the prompt template conditionally adds these fields to the required Review schema
when the flags are true.

pr_agent/settings/configuration.toml[74-90]
pr_agent/tools/pr_reviewer.py[82-96]
pr_agent/settings/pr_reviewer_prompts.toml[122-137]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The default configuration enables the new review assessment fields, changing the default prompt/output schema and increasing token usage for all users.
## Issue Context
These fields are conditionally included in the review prompt when the `require_*` flags are true, and PRReviewer passes those flags into the template variables.
## Fix Focus Areas
- pr_agent/settings/configuration.toml[84-86]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. review_rules_paths type unvalidated ✓ Resolved 📘 Rule violation ☼ Reliability
Description
review_rules_paths is only normalized when it's a string; other invalid types (e.g., int/dict from
env overrides) will cause a crash when iterated. This violates the requirement to validate/normalize
configuration inputs with safe defaults and targeted warnings.
Code

pr_agent/tools/pr_reviewer.py[R134-137]

+        rule_paths = get_settings().pr_reviewer.get("review_rules_paths", []) or []
+        if isinstance(rule_paths, str):
+            rule_paths = [rule_paths]
+
Evidence
Rule 19 requires configuration inputs to be validated and normalized with safe defaults. The added
code normalizes strings but can still crash on other invalid types when looping over rule_paths.

pr_agent/tools/pr_reviewer.py[134-137]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`review_rules_paths` is used as an iterable without full type validation/normalization. If the setting is overridden to a non-list, non-string type, `_get_review_rules()` can raise at runtime.
## Issue Context
The code only converts a string into a single-item list, but does not guard against other invalid types.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[134-137]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. GitLab rules never load ✓ Resolved 🐞 Bug ≡ Correctness
Description
PRReviewer._get_review_rules() can’t resolve a trusted base ref on GitLab because it only checks
pr.base.{sha,ref} or provider attributes base_sha/base_ref, which GitLabProvider doesn’t expose.
As a result, it always returns an empty rules string (and emits a warning), so repository review
rules are never injected for GitLab PR reviews.
Code

pr_agent/tools/pr_reviewer.py[R138-147]

+        base = getattr(getattr(self.git_provider, "pr", None), "base", None)
+        ref = (
+            getattr(base, "sha", None)
+            or getattr(base, "ref", None)
+            or getattr(self.git_provider, "base_sha", None)
+            or getattr(self.git_provider, "base_ref", None)
+        )
+        if not ref:
+            get_logger().warning("Could not resolve a trusted base ref for review rules")
+            return ""
Evidence
The new logic returns early when it can’t resolve ref from pr.base or base_sha/base_ref.
GitLabProvider’s .pr is the merge request object (not a GitHub-style PR with .base), and
GitLabProvider uses mr.diff_refs['base_sha'] as the correct base ref when loading file contents;
since _get_review_rules() doesn’t consider diff_refs, it will fail to find a ref on GitLab and
skip loading rules even when enabled.

pr_agent/tools/pr_reviewer.py[130-147]
pr_agent/git_providers/gitlab_provider.py[336-340]
pr_agent/git_providers/gitlab_provider.py[432-437]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PRReviewer._get_review_rules()` resolves a “trusted base ref” using `self.git_provider.pr.base` and a couple provider attributes. On GitLab, `git_provider.pr` is the merge request object (`mr`) and the provider relies on `mr.diff_refs['base_sha']` / `['head_sha']` for refs, so the current logic fails to find a ref and returns `""`, effectively disabling review rules.
### Issue Context
GitLabProvider exposes the MR via `.pr` and uses `mr.diff_refs['base_sha']` elsewhere to load file contents for diffs.
### Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[130-179]
### Implementation notes
- Extend `_get_review_rules()` ref resolution to handle GitLab by checking `getattr(self.git_provider.pr, "diff_refs", None)` (or `getattr(self.git_provider, "mr", None)`) and using `diff_refs.get("base_sha")` as the trusted ref.
- Keep the existing GitHub/Gitea logic intact; only add a GitLab-specific fallback when current resolution yields no ref.
- Ensure the selected ref is still “trusted” (base/target side), not the PR head.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. Persistent header string mismatch 🐞 Bug ≡ Correctness
Description
PRReviewer now uses an initial_header without the "🔍" suffix while convert_to_markdown_v2()
still emits a header that includes it. In persistent-comment mode this causes
publish_persistent_comment_full() to replace only the prefix and leave a stray "🔍" line in the
updated comment body.
Code

pr_agent/tools/pr_reviewer.py[R234-239]

if get_settings().pr_reviewer.persistent_comment and not self.incremental.is_incremental:
final_update_message = get_settings().pr_reviewer.final_update_message
self.git_provider.publish_persistent_comment(pr_review,
-                                                            initial_header=f"{PRReviewHeader.REGULAR.value} 🔍",
+                                                            initial_header=f"{PRReviewHeader.REGULAR.value}",
                                update_header=True,
                                final_update_message=final_update_message, )
Evidence
The review body generation prepends "## PR Reviewer Guide 🔍", while persistent updates
search/replace using the provided initial_header. With initial_header set to "## PR Reviewer
Guide" (no emoji), the replacement in publish_persistent_comment_full() will not remove the emoji
that remains immediately after the matched substring.

pr_agent/algo/utils.py[159-166]
pr_agent/tools/pr_reviewer.py[233-240]
pr_agent/git_providers/git_provider.py[309-316]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Persistent comment updates rely on exact string matching/replacement of the review header. The PR changed `initial_header` passed to `publish_persistent_comment(...)` to omit the "🔍" suffix, but the generated markdown header still includes it, causing a dangling emoji line after header update.
## Issue Context
- `convert_to_markdown_v2()` emits a header with "🔍".
- `publish_persistent_comment_full()` updates an existing comment by `startswith(initial_header)` and then doing `pr_comment.replace(initial_header, updated_header)`.
- If `initial_header` is a prefix of the generated header (missing the emoji), the replacement leaves the emoji behind.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[233-240]
- pr_agent/algo/utils.py[159-166]
- pr_agent/git_providers/git_provider.py[301-316]
## Suggested fix
Choose one of:
1) Restore `initial_header` to exactly match the generated header (include " 🔍"), or
2) Remove the emoji from the generated header in `convert_to_markdown_v2()` and keep `initial_header` emoji-free, or
3) Centralize header construction (single helper/constant) so both generation and persistent-update use the exact same string.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. review_priority_files lacks type guard ✓ Resolved 📘 Rule violation ☼ Reliability
Description
convert_to_markdown_v2() iterates value for review_priority_files without verifying it is a
list of strings, so malformed model output (e.g., a string/dict) can render incorrectly or raise
runtime errors. This violates the requirement to use defensive access for optional/variable external
structures.
Code

pr_agent/algo/utils.py[R255-262]

+        elif "Review priority files" in key_nice.lower():
+            if not value:
+                markdown_text += f"### {emoji} Priority files: None\n\n"
+            else:
+                markdown_text += f"### {emoji} Priority files\n\n"
+                for priority_file in value:
+                    markdown_text += f"- {priority_file}\n"
+                markdown_text += "\n"
Evidence
PR Compliance ID 18 requires defensive access/type checks for variable external structures. The new
branch iterates for priority_file in value: without checking isinstance(value, list) (or
coercing non-list types), risking TypeError/incorrect iteration when the model output deviates from
the expected schema.

pr_agent/algo/utils.py[255-262]
Best Practice: Learned patterns

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`convert_to_markdown_v2()` assumes `review_priority_files` is an iterable list and directly iterates it. Since this value comes from model-generated YAML, it can be missing or the wrong type, which can lead to incorrect rendering (e.g., iterating characters of a string) or exceptions.
## Issue Context
This output is derived from an AI prediction and should be treated as an external/variable structure.
## Fix Focus Areas
- pr_agent/algo/utils.py[255-262]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


12. keys_fix_yaml line exceeds 120 ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The updated keys_fix_yaml=[...] line exceeds the 120-character Ruff line-length requirement,
risking lint/pre-commit failures. This introduces a style violation in modified Python code.
Code

pr_agent/tools/pr_reviewer.py[R297-300]

data = load_yaml(self.prediction.strip(),
-                         keys_fix_yaml=["ticket_compliance_check", "estimated_effort_to_review_[1-5]:", "security_concerns:", "key_issues_to_review:",
+                         keys_fix_yaml=["ticket_compliance_check", "estimated_effort_to_review_[1-5]:", "risk_level:", "merge_recommendation:", "security_concerns:", "key_issues_to_review:",
          "relevant_file:", "relevant_line:", "suggestion:"],
first_key=first_key, last_key=last_key)
Evidence
PR Compliance ID 8 requires Python changes to respect Ruff line-length = 120. The
keys_fix_yaml=[...] argument was modified to include new entries and is now a very long line that
exceeds this limit.

AGENTS.md
pr_agent/tools/pr_reviewer.py[297-300]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A modified Python line now exceeds Ruff's configured 120-character limit, which can fail CI/linting.
## Issue Context
The repository compliance checklist requires Ruff style conformance for Python changes.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[297-300]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


13. YAML repair key mismatch ✓ Resolved 🐞 Bug ☼ Reliability
Description
PRReviewer._prepare_pr_review() passes keys_fix_yaml entries with inconsistent formatting,
including "ticket_compliance_check" without a trailing colon, but try_fix_yaml() does a raw
substring replacement; this can corrupt lines like ticket_compliance_check: into invalid YAML
during fallback parsing and prevent /review from publishing.
Code

pr_agent/tools/pr_reviewer.py[R295-300]

first_key = 'review'
last_key = 'security_concerns'
data = load_yaml(self.prediction.strip(),
-                         keys_fix_yaml=["ticket_compliance_check", "estimated_effort_to_review_[1-5]:", "security_concerns:", "key_issues_to_review:",
+                         keys_fix_yaml=["ticket_compliance_check", "estimated_effort_to_review_[1-5]:", "risk_level:", "merge_recommendation:", "security_concerns:", "key_issues_to_review:",
          "relevant_file:", "relevant_line:", "suggestion:"],
first_key=first_key, last_key=last_key)
Evidence
The keys_fix_yaml list passed from PRReviewer._prepare_pr_review() includes
ticket_compliance_check without a colon, while the YAML repair logic in try_fix_yaml() searches
for each key as a substring and replaces it inline, which can split a key: token incorrectly and
produce invalid YAML during fallback parsing.

pr_agent/tools/pr_reviewer.py[295-300]
pr_agent/algo/utils.py[796-816]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`load_yaml(..., keys_fix_yaml=[...])` is given a mix of key strings with and without `:` (e.g., `"ticket_compliance_check"` vs `"risk_level:"`). In `try_fix_yaml()`, fallback #1 performs a naive substring replacement on any line containing each key, which can corrupt otherwise-correct YAML keys that already include `:` (e.g., turning `ticket_compliance_check:` into `ticket_compliance_check |\n        :`). This reduces recovery success when the initial YAML parse fails.
## Issue Context
- The problematic behavior happens only on YAML parse failures, but that’s exactly when the fallbacks must be most robust.
- The corruption risk is introduced/expanded by the updated `keys_fix_yaml` list in `PRReviewer._prepare_pr_review()`.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[295-300]
### Recommended fix
- Make `keys_fix_yaml` entries consistently match YAML keys, e.g. use `"ticket_compliance_check:"` (with colon) or remove this key from fallback rewriting if it represents a structured (list/dict) field.
- (Optional hardening) Update `try_fix_yaml()` to only rewrite actual YAML keys at line starts using a regex like `r"^(\s*)<key>(\s*:)"` instead of substring search, so it doesn’t rewrite substrings inside other content.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


14. Priority files condition unreachable ✓ Resolved 📘 Rule violation ≡ Correctness
Description
The new convert_to_markdown_v2() branch intended to render review_priority_files is effectively
dead code because it checks for a capitalized/mixed-case substring against key_nice.lower(), so it
can never match. As a result, review_priority_files falls through to the generic renderer instead
of being formatted as the intended priority-files list/output.
Code

pr_agent/algo/utils.py[R255-262]

+        elif "Review priority files" in key_nice.lower():
+            if not value:
+                markdown_text += f"### {emoji} Priority files: None\n\n"
+            else:
+                markdown_text += f"### {emoji} Priority files\n\n"
+                for priority_file in value:
+                    markdown_text += f"- {priority_file}\n"
+                markdown_text += "\n"
Evidence
PR Compliance ID 2 prohibits adding dead code, and the citations show that
convert_to_markdown_v2() lowercases key_nice for matching (key_nice.lower()), but the
condition for the priority-files path uses the mixed/capitalized literal "Review priority files".
Because the string being searched is always lowercase while the substring contains uppercase
letters, the elif condition is unreachable, leaving the newly added review_priority_files
formatting branch inactive and preventing the intended rendering.

Rule 2: No Dead or Commented-Out Code
pr_agent/algo/utils.py[255-262]
pr_agent/algo/utils.py[175-180]
pr_agent/algo/utils.py[255-263]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The `review_priority_files` rendering branch in `convert_to_markdown_v2()` is unreachable because it performs a case-sensitive substring search using a mixed/capitalized literal (e.g., `"Review priority files"`) against `key_nice.lower()`, which is always lowercase.
## Issue Context
`key_nice` is transformed and then compared via `key_nice.lower()`. With the current mixed-case substring in the condition, the match can never succeed, so the new priority-files rendering path never runs and `review_priority_files` falls through to the generic renderer instead of being formatted as the intended priority-files list/output.
## Fix Focus Areas
- pr_agent/algo/utils.py[175-180]
- pr_agent/algo/utils.py[255-263]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


15. Extra blank lines in imports ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The PR introduces three consecutive blank lines within the import block, which is likely to fail
Ruff/PEP8 blank-line rules and create unnecessary diff noise. This can break linting CI and violates
the repository formatting conventions.
Code

pr_agent/tools/pr_reviewer.py[R4-7]

+
+
+
from collections import OrderedDict
Evidence
PR Compliance ID 8 requires adhering to Ruff formatting conventions. The modified import section
contains three consecutive blank lines before the from collections import OrderedDict line, which
is inconsistent with typical Ruff/PEP8 import formatting and may cause lint failures.

AGENTS.md
pr_agent/tools/pr_reviewer.py[2-9]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
There are too many consecutive blank lines inside the imports section, which can violate Ruff/PEP8 formatting rules.
## Issue Context
The file currently has three blank lines between `import traceback` and the next import group.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[4-7]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


16. Headings break GFM table ✓ Resolved 🐞 Bug ≡ Correctness
Description
When gfm_supported is true, convert_to_markdown_v2() opens an HTML ` but the new risk_level`
and merge_recommendation branches emit Markdown headings (### ...) rather than .... This
produces malformed mixed HTML/Markdown output and can render the review comment unpredictably.
Code

pr_agent/algo/utils.py[R241-253]

+        elif "risk level" in key_nice.lower():
+            risk_value = str(value).strip().lower().replace("_", " ")
+            risk_display = risk_value.capitalize() if risk_value else "Unknown"
+            markdown_text += f"### {emoji} Risk level: {risk_display}\n\n"
+
+        elif "merge recommendation" in key_nice.lower():
+            recommendation = str(value).strip().replace("_", " ")
+            recommendation_display = (
+                recommendation.capitalize() if recommendation else "Unknown"
+            )
+            markdown_text += (
+                f"### {emoji} Merge recommendation: {recommendation_display}\n\n"
+            )
Evidence
The function opens a `` in GFM mode and closes it only at the end, but the new risk/merge branches
emit ### headings in the middle of the table content instead of table rows.

pr_agent/algo/utils.py[171-173]
pr_agent/algo/utils.py[241-253]
pr_agent/algo/utils.py[347-349]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`convert_to_markdown_v2()` writes markdown headings for `risk_level` / `merge_recommendation` even while a `<table>` is open in GFM mode, breaking the table structure.
## Issue Context
The function opens `<table>` when `gfm_supported` is true, and most branches emit `<tr><td>...</td></tr>`. The new branches should follow the same pattern (or close the table before emitting headings).
## Fix Focus Areas
- pr_agent/algo/utils.py[171-173]
- pr_agent/algo/utils.py[241-253]
- pr_agent/algo/utils.py[347-349]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


17. Untrusted PR review rules ✓ Resolved 🐞 Bug ⛨ Security
Description
PRReviewer._get_review_rules() loads rules from the PR head SHA/branch, so a PR author can
add/modify a rules file in the same PR and have it injected into the model prompt. This allows
author-controlled prompt modifications that can bias/suppress the review output.
Code

pr_agent/tools/pr_reviewer.py[R138-151]

+        ref = getattr(getattr(self.git_provider, 'pr', None), 'head', None)
+        ref = getattr(ref, 'sha', None) or self.git_provider.get_pr_branch()
+        loaded_rules = []
+        loaded_rule_paths = []
+
+        for rule_path in rule_paths:
+            try:
+                rule_content = self.git_provider.get_pr_file_content(rule_path, ref)
+            except Exception:
+                continue
+
+            if rule_content and rule_content.strip():
+                loaded_rule_paths.append(rule_path)
+                loaded_rules.append(f"File: `{rule_path}`\n{rule_content.strip()}")
Evidence
_get_review_rules() explicitly picks the PR head SHA (or PR source branch) as ref and reads rule
files at that ref; the prompt template injects {{ review_rules }} into the model instructions; the
GitHub provider fetches file contents at ref=branch, so the head SHA/branch is authoritative for
the rules content.

pr_agent/tools/pr_reviewer.py[130-151]
pr_agent/settings/pr_reviewer_prompts.toml[71-80]
pr_agent/git_providers/github_provider.py[867-876]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Review rules are fetched at the PR head ref and injected into the review prompt, allowing PR authors to change the reviewing instructions via a file included in their PR.
## Issue Context
`_get_review_rules()` chooses `pr.head.sha` (or source branch) as the ref for `get_pr_file_content()`, and `pr_reviewer_prompts.toml` injects `review_rules` into the prompt.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[130-171]
- pr_agent/settings/pr_reviewer_prompts.toml[71-80]
- pr_agent/git_providers/github_provider.py[867-876]
## Suggested direction
Resolve a trusted ref for rules (e.g., PR base SHA / default branch) and load rules from that ref, not from the PR head. If a trusted ref cannot be determined for a provider, disable rules loading (with a clear log) rather than falling back to the PR head.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


18. convert_to_markdown_v2 uses single quotes ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
New Python code introduces several single-quoted string literals (e.g., 'Unknown' and in
getattr(..., 'pr', ...) / getattr(..., 'sha', ...)) instead of the project’s double-quote
convention. This can violate Ruff formatting/lint expectations and create inconsistent style in
modified code.
Code

pr_agent/algo/utils.py[R239-248]

+        elif 'risk level' in key_nice.lower():
+            risk_value = str(value).strip().lower().replace('_', ' ')
+            risk_display = risk_value.capitalize() if risk_value else 'Unknown'
+            if gfm_supported:
+                markdown_text += f"<tr><td><strong>Risk level</strong>: {risk_display}</td></tr>\n"
+            else:
+                markdown_text += f"### Risk level: {risk_display}\n\n"
+        elif 'merge recommendation' in key_nice.lower():
+            recommendation = str(value).strip().replace('_', ' ')
+            recommendation_display = recommendation.capitalize() if recommendation else 'Unknown'
Evidence
PR Compliance ID 9 requires Python changes to follow Ruff/isort style, including using double quotes
for string literals. The cited modified areas—both the risk level/merge recommendation rendering
and the newly added _get_review_rules() block—contain newly introduced single-quoted strings,
demonstrating a direct mismatch with the mandated double-quote convention.

AGENTS.md
pr_agent/algo/utils.py[239-248]
pr_agent/tools/pr_reviewer.py[130-140]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New Python changes introduce single-quoted string literals in modified code paths, which conflicts with the repository’s Ruff/isort style requirements (including the double-quote convention for Python strings) and may cause formatting/lint failures.
## Issue Context
PR Compliance ID 9/checklist guidance explicitly requires Ruff-style compliance for Python changes, including using double quotes for string literals. Recent edits in both markdown conversion/rendering logic and the `_get_review_rules()` implementation added single-quoted strings (e.g., `'Unknown'`, and `getattr(..., 'pr', ...)` / `getattr(..., 'sha', ...)`) that should be converted to double quotes.
## Fix Focus Areas
- pr_agent/algo/utils.py[239-248]
- pr_agent/tools/pr_reviewer.py[130-140]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

19. _get_review_rules() swallows exceptions 📘 Rule violation ☼ Reliability ⭐ New
Description
_get_review_rules() catches Exception when fetching rule files and silently continues, which can
hide provider bugs/auth failures and make review-rules behavior non-deterministic and hard to debug.
This is contrary to robust error handling expectations because failures are ignored without any
actionable logging or fallback distinction (e.g., missing file vs. provider error).
Code

pr_agent/tools/pr_reviewer.py[R155-160]

+        for rule_path in rule_paths:
+            try:
+                rule_content = self.git_provider.get_pr_file_content(rule_path, ref)
+            except Exception:
+                continue
+
Evidence
PR Compliance ID 3 requires errors to be handled with appropriate responses rather than ignored. The
new code catches all exceptions during rules-file retrieval and continues without logging or
differentiating failure modes, effectively ignoring errors.

Rule 3: Robust Error Handling
pr_agent/tools/pr_reviewer.py[155-160]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PRReviewer._get_review_rules()` silently swallows exceptions when calling `git_provider.get_pr_file_content(...)` and continues, which can mask real failures (permission/network/provider API errors) and makes it unclear why rules were not loaded.

## Issue Context
This code is executed when `enable_review_rules` is enabled. Distinguish between “file not found / empty” and “unexpected provider failure”, and emit actionable logs (ideally with exception details) rather than ignoring errors.

## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[155-160]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


20. GitLab rules ref unstable 🐞 Bug ☼ Reliability ⭐ New
Description
GitLabProvider.get_pr_base_ref() returns the target branch name, so review rules are loaded from
the branch tip, while other GitLab logic (diff file loading) uses the MR’s stable
diff_refs['base_sha']. This can make injected review rules inconsistent with the code snapshot
being reviewed if the target branch advances between MR creation and review execution.
Code

pr_agent/git_providers/gitlab_provider.py[R777-779]

+    def get_pr_base_ref(self) -> str:
+        return getattr(self.mr, "target_branch", "") or ""
+
Evidence
GitLabProvider currently exposes base ref as target_branch, but elsewhere it clearly treats
mr.diff_refs['base_sha'] as the canonical base snapshot for fetching original content. PRReviewer
uses get_pr_base_ref() as the ref to load rules content, so using a branch name can load from a
different commit than the diff base.

...

Comment thread pr_agent/tools/pr_reviewer.py Outdated
Comment thread pr_agent/tools/pr_reviewer.py Outdated
Comment thread pr_agent/tools/pr_reviewer.py Outdated
Comment thread pr_agent/tools/pr_reviewer.py
Comment thread pr_agent/algo/utils.py Outdated
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 14, 2026

Persistent review updated to latest commit 104a696

Comment thread pr_agent/tools/pr_reviewer.py Outdated
Comment thread pr_agent/tools/pr_reviewer.py Outdated
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 14, 2026

Code Review by Qodo

Grey Divider

Sorry, something went wrong

We weren't able to complete the code review on our side. Please try again

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit fc8ee1d

Comment thread pr_agent/tools/pr_reviewer.py Outdated
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit d8cbb89

Comment thread pr_agent/tools/pr_reviewer.py
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit e237f87

Comment thread pr_agent/tools/pr_reviewer.py
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit 13136c6

Comment thread pr_agent/tools/pr_reviewer.py Outdated
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit 03e0a0d

Comment thread pr_agent/algo/utils.py Outdated
Comment thread pr_agent/algo/utils.py Outdated
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit 2d099fa

Comment thread pr_agent/algo/utils.py Outdated
Comment on lines +255 to +262
elif "Review priority files" in key_nice.lower():
if not value:
markdown_text += f"### {emoji} Priority files: None\n\n"
else:
markdown_text += f"### {emoji} Priority files\n\n"
for priority_file in value:
markdown_text += f"- {priority_file}\n"
markdown_text += "\n"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Priority files condition unreachable 📘 Rule violation ≡ Correctness

The new convert_to_markdown_v2() branch intended to render review_priority_files is effectively
dead code because it checks for a capitalized/mixed-case substring against key_nice.lower(), so it
can never match. As a result, review_priority_files falls through to the generic renderer instead
of being formatted as the intended priority-files list/output.
Agent Prompt
## Issue description
The `review_priority_files` rendering branch in `convert_to_markdown_v2()` is unreachable because it performs a case-sensitive substring search using a mixed/capitalized literal (e.g., `"Review priority files"`) against `key_nice.lower()`, which is always lowercase.

## Issue Context
`key_nice` is transformed and then compared via `key_nice.lower()`. With the current mixed-case substring in the condition, the match can never succeed, so the new priority-files rendering path never runs and `review_priority_files` falls through to the generic renderer instead of being formatted as the intended priority-files list/output.

## Fix Focus Areas
- pr_agent/algo/utils.py[175-180]
- pr_agent/algo/utils.py[255-263]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +4 to 7



from collections import OrderedDict
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Extra blank lines in imports 📘 Rule violation ⚙ Maintainability

The PR introduces three consecutive blank lines within the import block, which is likely to fail
Ruff/PEP8 blank-line rules and create unnecessary diff noise. This can break linting CI and violates
the repository formatting conventions.
Agent Prompt
## Issue description
There are too many consecutive blank lines inside the imports section, which can violate Ruff/PEP8 formatting rules.

## Issue Context
The file currently has three blank lines between `import traceback` and the next import group.

## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[4-7]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread pr_agent/algo/utils.py
Comment on lines +241 to +253
elif "risk level" in key_nice.lower():
risk_value = str(value).strip().lower().replace("_", " ")
risk_display = risk_value.capitalize() if risk_value else "Unknown"
markdown_text += f"### {emoji} Risk level: {risk_display}\n\n"

elif "merge recommendation" in key_nice.lower():
recommendation = str(value).strip().replace("_", " ")
recommendation_display = (
recommendation.capitalize() if recommendation else "Unknown"
)
markdown_text += (
f"### {emoji} Merge recommendation: {recommendation_display}\n\n"
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Headings break gfm table 🐞 Bug ≡ Correctness

When gfm_supported is true, convert_to_markdown_v2() opens an HTML <table> but the new
risk_level and merge_recommendation branches emit Markdown headings (### ...) rather than
<tr><td>...</td></tr>. This produces malformed mixed HTML/Markdown output and can render the
review comment unpredictably.
Agent Prompt
## Issue description
`convert_to_markdown_v2()` writes markdown headings for `risk_level` / `merge_recommendation` even while a `<table>` is open in GFM mode, breaking the table structure.

## Issue Context
The function opens `<table>` when `gfm_supported` is true, and most branches emit `<tr><td>...</td></tr>`. The new branches should follow the same pattern (or close the table before emitting headings).

## Fix Focus Areas
- pr_agent/algo/utils.py[171-173]
- pr_agent/algo/utils.py[241-253]
- pr_agent/algo/utils.py[347-349]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread pr_agent/tools/pr_reviewer.py Outdated
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit 71011b3

Comment thread pr_agent/tools/pr_reviewer.py
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit 9298146

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit 20673da

@xiaodeng666a
Copy link
Copy Markdown
Author

I’ve addressed the automated review feedback, added targeted tests, and narrowed the scope to structured review assessment plus optional repository review rules. At this point the persistent review is updating to the latest commit, so I’ll pause here unless you’d like any further changes.

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit d2f6982

Comment thread pr_agent/tools/pr_reviewer.py
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit 6020941

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit ca9ab5e

Comment thread pr_agent/git_providers/gitea_provider.py
@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit c133868

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit b2b3f6a

@qodo-free-for-open-source-projects
Copy link
Copy Markdown
Contributor

qodo-free-for-open-source-projects Bot commented May 15, 2026

Persistent review updated to latest commit fecf295

@CzechExplorer
Copy link
Copy Markdown

Error running review: Traceback (most recent call last):
File "/home/homelab/ensemble-review/ensemble_review.py", line 285, in
main()
File "/home/homelab/ensemble-review/ensemble_review.py", line 262, in main
diff = get_pr_diff(owner, repo, pr_number)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/homelab/ensemble-review/ensemble_review.py", line 65, in get_pr_diff
response.raise_for_status()
File "/home/homelab/.local/lib/python3.12/site-packages/requests/models.py", line 1028, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/pr-agent/pull/pulls/2391

1 similar comment
@CzechExplorer
Copy link
Copy Markdown

Error running review: Traceback (most recent call last):
File "/home/homelab/ensemble-review/ensemble_review.py", line 285, in
main()
File "/home/homelab/ensemble-review/ensemble_review.py", line 262, in main
diff = get_pr_diff(owner, repo, pr_number)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/homelab/ensemble-review/ensemble_review.py", line 65, in get_pr_diff
response.raise_for_status()
File "/home/homelab/.local/lib/python3.12/site-packages/requests/models.py", line 1028, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/pr-agent/pull/pulls/2391

@CzechExplorer
Copy link
Copy Markdown

📋 Ensemble PR Review

PR: Add review risk assessment and optional repository review rules
URL: #2391
Author: @xiaodeng666a


🟠 Gemini 3.1 Flash

Summary

This PR enhances the PR-Agent review process by introducing two main features: a structured risk assessment (risk level, merge recommendation, and priority files) and the ability to inject repository-specific review rules from markdown files (e.g., .pr_agent/review_rules.md). The approach is sound, maintaining backward compatibility while providing more actionable insights for human reviewers.


Issues Found

1. Logic / Robustness: Missing get_pr_file_content in several providers

The PR adds _get_review_rules to PRReviewer, which calls self.git_provider.get_pr_file_content(...).

  • Observation: You implemented this method for BitbucketServerProvider and GiteaProvider, but not for GithubProvider, GitlabProvider, or BitbucketProvider.
  • Impact: Since GitProvider (the base class) does not define this method, calling it on a GitHub or GitLab PR will raise an AttributeError, crashing the review process if enable_review_rules is true.
  • Location: pr_agent/git_providers/ (various files).

2. Logic: get_pr_base_ref implementation in GitLab

  • Location: pr_agent/git_providers/gitlab_provider.py:778
  • Issue: The implementation returns target_branch. In many CI/CD or bot contexts, using the branch name (e.g., main) to fetch a file is less "trusted" than using a specific SHA, especially if the branch has moved. While acceptable for a first pass, it differs from the GitHub implementation which tries sha first.

3. Bug: YAML Parsing last_key

  • Location: pr_agent/tools/pr_reviewer.py:300
  • Issue: You updated the keys_fix_yaml list but left last_key = "security_concerns". If the model places risk_level or merge_recommendation after security concerns in the YAML (which can happen depending on model temperature/shuffling), the load_yaml utility might truncate the output prematurely.
  • Suggestion: Ensure last_key matches the actual last field expected in the Pydantic schema or is set to a value that encompasses all new fields.

Suggestions

  1. Abstract Method Definition: Add get_pr_file_content as an @abstractmethod in git_provider.py to ensure all providers implement it, preventing runtime AttributeError.
  2. GitHub/GitLab Implementation:
    • For GitHub: Use self.repo.get_contents(path, ref=branch).decoded_content.
    • For GitLab: Use self.project.files.get(file_path=file_path, ref=branch).decode().
  3. Token Clipping Warning: In _get_review_rules, if review_rules is clipped via clip_tokens, it might be helpful to log a warning or add a note to the prompt so the LLM knows it is looking at a truncated set of rules.
  4. Formatting: In pr_agent/algo/utils.py:242, the code uses .replace("_", " "). This is good, but ensure that the emojis and headers align with the existing style. The current implementation looks consistent with convert_to_markdown_v2.

Rating: 7/10

The PR provides high-value features and the prompt engineering for the new fields is well-defined. However, it is currently not merge-ready because it will crash on GitHub and GitLab (the most common providers) due to the missing get_pr_file_content implementations.


Confidence: High

I have verified the method signatures across the git provider inheritance chain and confirmed the missing methods in the provided diff.


🔷 GPT-5 Mini

  1. Summary
  • This PR adds structured review outputs (risk_level, merge_recommendation, review_priority_files) to the review schema, templates, markdown renderer, and tests. It also implements optional repository/team "review rules" loading (configurable file paths, clipping by tokens) and injects the rules into the reviewer prompt. Multiple git provider implementations were extended with get_pr_base_ref and get_pr_file_content helpers. The approach is generally sensible: it keeps defaults backward-compatible and uses small, focused changes to templates, rendering, settings, and the PRReviewer tool to load rules.
  1. Issues Found
  • Potential inconsistent base ref semantics across providers:
    • pr_agent/git_providers/bitbucket_server_provider.py: get_pr_base_ref returns self.pr.toRef.get("latestCommit") or self.pr.toRef.get("displayId") or "" (line ~421). latestCommit is a commit SHA, displayId is a branch name; other providers return sha or ref inconsistenly. This may be fine but could lead to requesting file content from a commit SHA vs branch name differences depending on provider implementation. Review the expectations of get_file/get_pr_file_content in each provider.
  • Missing get_pr_file_content on some providers:
    • You added get_pr_file_content to bitbucket_server_provider and gitea_provider, but not to github_provider, gitlab_provider, bitbucket_provider. PRReviewer._get_review_rules calls self.git_provider.get_pr_file_content(rule_path, ref) for every provider. If a provider does not implement get_pr_file_content, Python will raise AttributeError at runtime. Specifically:
      • pr_agent/git_providers/github_provider.py: no get_pr_file_content defined (line ~701 change only added get_pr_base_ref).
      • pr_agent/git_providers/bitbucket_provider.py: no get_pr_file_content defined.
      • pr_agent/git_providers/gitlab_provider.py: no get_pr_file_content defined.
    • There is a default get_file method on some providers but PRReviewer expects get_pr_file_content to exist. This is a likely runtime bug.
  • Logging of warnings as exceptions:
    • In PRReviewer._prepare_pr_review you call get_logger().exception when parsing fails: get_logger().exception("Failed to parse review data", artifact={"data": data}) (unchanged). Using exception() logs stacktrace; acceptable, but keep in mind.
  • convert_to_markdown_v2 key-skipping logic:
    • In pr_agent/algo/utils.py you modified the check that filters out empty fields to include 'review_priority_files' so it doesn't get skipped early. This is ok, but the code does string-lower compare to ['can_be_split','key_issues_to_review','review_priority_files']; if future keys are added they must be mirrored. Not an immediate bug, but fragile.
  • Tests assume clip_tokens exists and works as used:
    • PRReviewer._get_review_rules uses clip_tokens(review_rules, max_tokens). clip_tokens is imported in pr_reviewer import, but ensure clip_tokens limits by token count not characters; tests don't assert token clipping though. OK.
  • Small formatting: in pr_agent/tools/pr_reviewer.py you removed the 🔍 emoji in the persistent comment header (line ~173 change). That changes user-visible output; likely intentional, but worth calling out as change in UX.
  • Tests do not mock clip_tokens nor get_logger behavior; they patch get_settings correctly, but some tests use reviewer.git_provider.get_pr_file_content.side_effect = _get_file_content — this assumes PRReviewer has that attribute; tests construct reviewer via bypassing init and set git_provider, so ok. But no tests for provider fallback when get_pr_file_content missing.
  1. Suggestions
  • Add a default get_pr_file_content to the abstract base class (pr_agent/git_providers/git_provider.py) that calls get_file(file_path, branch) or raises NotImplementedError with descriptive message. That avoids AttributeError and centralizes behavior. Example: def get_pr_file_content(self, file_path, branch): try: return self.get_file(file_path, branch) except Exception: warning... return "".
  • Alternatively implement get_pr_file_content in all providers (github/gitlab/bitbucket) following pattern used in gitea/bitbucket_server to ensure consistent behavior.
  • Normalize get_pr_base_ref return value contract: decide whether get_pr_base_ref returns commit SHA or branch name (ref) and document it. Then update providers to follow the same contract. This matters because get_file/get_pr_file_content implementations may expect a branch name or sha.
  • Make PRReviewer._get_review_rules resilient if git_provider lacks get_pr_file_content: use getattr(self.git_provider, "get_pr_file_content", self.git_provider.get_file) or similar fallback.
  • Add tests that exercise the fallback behavior when a provider does not have get_pr_file_content (to prevent runtime failures).
  • Consider sanitizing/limiting the combined size of review_rules in characters as well as tokens to avoid constructing excessively long prompt pieces inadvertently (or ensure clip_tokens prevents huge payloads).
  • The new settings flags default to false in configuration.toml, but templates require these flags to be referenced via get_settings().pr_reviewer.get(...). You already use get with defaults in pr_reviewer.init for the new flags — good. Ensure documentation/README updated to mention enable_review_rules and paths.
  • Minor: in convert_to_markdown_v2 the key detection uses key.replace('_', ' ').capitalize() then later uses "if 'merge recommendation' in key_nice.lower()": this is ok but fragile. Could be simplified by checking the original key names (snake_case) instead of humanized key_nice.
  • Add unit tests that ensure the reviewer prompt receives the review_rules when enabled (i.e., verify that PRReviewer.init places review_rules into the prompt context), and a test to ensure clip_tokens was applied when max_review_rules_tokens > 0.
  1. Rating: 6/10
  • Justification: The feature is well-scoped and tests were added for the markdown rendering and the review rules loader, but there is a significant runtime risk because PRReviewer assumes git_provider.get_pr_file_content exists for all providers while only some providers implement it. Also providers return inconsistent base ref types which may cause subtle bugs. After addressing provider API consistency and adding a few more tests and a small fallback, this PR could be merge-ready.
  1. Confidence: High

🔵 DeepSeek V4 Flash

Summary
This PR introduces structured review outputs (risk level, merge recommendation, priority files) and an optional mechanism to load repository-specific review rules from configurable markdown files. The overall approach is sound and integrates cleanly with the existing review pipeline, provided the feature is disabled by default. However, the implementation is incomplete across git providers, which will cause silent failures when the feature is enabled on providers that lack the required file‑retrieval method.


Issues Found

  1. Missing get_pr_file_content in multiple providers
    The new _get_review_rules calls self.git_provider.get_pr_file_content(rule_path, ref), but this method is only implemented for Gitea and Bitbucket Server in this PR. The GitHub, GitLab, and Bitbucket (cloud) providers do not define it, and the abstract base class GitProvider (file pr_agent/git_providers/git_provider.py) has no such method.

    • When enable_review_rules = true on an affected provider, the call will raise an AttributeError.
    • The except Exception block in _get_review_rules catches it silently, preventing a crash but leaving the feature completely non‑functional without any log or indication.
    • Files: pr_agent/tools/pr_reviewer.py line ~155, provider files (e.g. github_provider.py, gitlab_provider.py, bitbucket_provider.py).
  2. No fallback in base class
    git_provider.py (line 181) adds get_pr_base_ref with a default warning, but no corresponding default for get_pr_file_content. All provider classes should at least inherit a safe no‑op that returns an empty string, so the feature degrades gracefully everywhere.

  3. Silent exception swallowing
    In _get_review_rules, the try...except Exception: continue loop hides any error when loading a rule file (including the AttributeError above). Even if the method is implemented, genuine failures (network, permission, etc.) are silently ignored instead of being logged.

    • File: pr_agent/tools/pr_reviewer.py line ~150.
  4. Persistent comment header changed unexpectedly
    The emoji 🔍 was removed from the persistent comment header (PRReviewHeader.REGULAR.value). While minor, it alters the user‑facing output. No justification is provided in the description; this may be an accidental change.

    • File: pr_agent/tools/pr_reviewer.py line ~174.

Suggestions

  • Add a default get_pr_file_content(path: str, branch: str) -> str to GitProvider that logs a warning and returns "". Then override it where a real implementation is possible (GitHub, GitLab, Bitbucket Server, Gitea).
  • For GitHub, a simple wrapper around its existing get_file method would suffice; similarly for GitLab using the repository file API.
  • Log an explicit warning when a rule file fails to load (instead of bare continue), so operators can diagnose missing or misconfigured rules.
  • Ensure the test suite covers the scenario where a provider does not implement get_pr_file_content (e.g., mock a provider with only the base methods).
  • Restore the emoji in the persistent comment header unless the removal was intentional; the PR description should mention any UI changes.
  • Consider guarding the feature with a more explicit check: if hasattr(self.git_provider, 'get_pr_file_content'): and log a clear message if missing, rather than relying on exception handling.

Rating: 6/10
The enhancements are well‑designed and backward‑compatible, but the provider coverage gap makes the “review rules” feature unusable on several major platforms. The PR needs a small, focused fix to be truly merge‑ready.

Confidence: High
The missing method and its downstream effects are evident from the diff and the abstract class structure.


🤖 Review generated via ensemble of 3 models (DeepSeek V4 Flash, Gemini 3 Flash, GPT-5 Mini)

@CzechExplorer
Copy link
Copy Markdown

Error running review: Traceback (most recent call last):
File "/home/homelab/ensemble-review/ensemble_review.py", line 285, in
main()
File "/home/homelab/ensemble-review/ensemble_review.py", line 262, in main
diff = get_pr_diff(owner, repo, pr_number)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/homelab/ensemble-review/ensemble_review.py", line 65, in get_pr_diff
response.raise_for_status()
File "/home/homelab/.local/lib/python3.12/site-packages/requests/models.py", line 1028, in raise_for_status
raise HTTPError(http_error_msg, response=self)
requests.exceptions.HTTPError: 404 Client Error: Not Found for url: https://api.github.com/repos/pr-agent/pull/pulls/2391

@CzechExplorer
Copy link
Copy Markdown

📋 Ensemble PR Review

PR: Add review risk assessment and optional repository review rules
URL: #2391
Author: @xiaodeng666a


🟠 Gemini 3.1 Flash

PR Review Summary

Summary
This PR enhances the pr-agent review process by introducing two main features: structured risk assessment (risk level, merge recommendation, and priority files) and the ability to load repository-specific review rules from markdown files. The approach is sound, utilizing Pydantic-like definitions in prompts and expanding the git_provider interface to fetch configuration files from the base branch.


Issues Found

1. Logic Error: Missing get_pr_file_content in several providers

  • File: pr_agent/git_providers/bitbucket_provider.py, pr_agent/git_providers/github_provider.py, pr_agent/git_providers/gitlab_provider.py
  • Issue: The PR adds _get_review_rules to PRReviewer, which calls self.git_provider.get_pr_file_content(rule_path, ref). However, this method was only implemented for BitbucketServerProvider and GiteaProvider.
  • Consequence: Calling review on GitHub, GitLab, or standard Bitbucket will likely result in an AttributeError because the base GitProvider class does not define this method (not even as an abstract method), and the specific providers haven't implemented it.

2. Logic Error: last_key in _prepare_pr_review might truncate output

  • File: pr_agent/tools/pr_reviewer.py, line 300
  • Issue: The last_key is set to "security_concerns". In load_yaml, this is often used to stop parsing or validate the end of the expected schema.
  • Consequence: If the LLM places the new fields (risk_level, merge_recommendation, etc.) after security_concerns in the YAML string, they might be ignored or cause parsing issues depending on how load_yaml handles the last_key constraint.

3. Potential Runtime Error: get_settings().pr_reviewer.get(...)

  • File: pr_agent/tools/pr_reviewer.py, lines 91-93
  • Issue: The code uses get_settings().pr_reviewer.get("require_risk_assessment", False).
  • Consequence: Depending on the dynaconf setup, if pr_reviewer is an object/box rather than a dict, .get() might not behave as expected or might throw if the attribute is missing. Using getattr(get_settings().pr_reviewer, "require_risk_assessment", False) is generally safer in this codebase.

Suggestions

  1. Implement get_pr_file_content for all providers: Ensure GitHub, GitLab, and Bitbucket Cloud providers implement the file fetching logic. For GitHub, this would use repo.get_contents(path, ref=ref).
  2. Update GitProvider Interface: Add get_pr_file_content as an @abstractmethod in pr_agent/git_providers/git_provider.py to ensure all future providers implement it.
  3. Refine YAML parsing: Update the last_key in pr_agent/tools/pr_reviewer.py to the actual last field expected in the YAML (likely review_priority_files or suggestions depending on the configuration) to ensure no data is truncated.
  4. Prompting Hint: In pr_reviewer_prompts.toml, consider explicitly stating the allowed values for risk_level and merge_recommendation in the YAML example section as well, to reinforce the "exactly one of" constraint.

Rating: 7/10

The PR provides high-value features that make AI reviews much more professional and customizable. However, it is currently "broken" for the most popular providers (GitHub/GitLab) due to the missing implementation of get_pr_file_content. Once the provider methods are added, this would be a 9/10.

Confidence: High

I have reviewed the logic flow from the tool initialization to the prompt generation and the markdown conversion. The missing method implementations are a clear oversight in the current diff.


🔷 GPT-5 Mini

  1. Summary
  • Adds structured review fields (risk_level, merge_recommendation, review_priority_files) to the PR review schema and rendering, plus CLI settings to require/enable them.
  • Adds optional repository/team review rules: configurable file paths to load markdown guidance from the repository base ref and inject into the review prompt. Implements provider methods to read file content from PR base refs for several providers. Approach is reasonable and mostly consistent with existing patterns (templating flags, token clipping, provider extensions), and tests were added for the markdown rendering and review rules loader.
  1. Issues Found
  • Potential mismatch in get_pr_base_ref semantics and returned value type
    • Files: github_provider.py, gitlab_provider.py, bitbucket_provider.py, bitbucket_server_provider.py, gitea_provider.py, git_provider.py
    • Details: The new get_pr_base_ref methods return different things across providers: some return commit SHAs (GitHub returns self.pr.base.sha or self.pr.base.ref), Bitbucket Server returns latestCommit or displayId, Gitea returns base_sha or base_ref, GitLab returns target_branch. The PR reviewer expects a "trusted base ref for review rules" and passes it to get_pr_file_content. For providers where get_pr_file_content (or underlying repo file fetching) expects a branch name (ref like 'main' or branch ref) but the code sometimes returns a commit SHA (e.g. github_provider returns base.sha first), this may still work for file retrieval but can be surprising. Also git_provider.get_pr_base_ref default returns "" but logs a warning which may spam logs.
  • Missing get_pr_file_content on some providers
    • Files: bitbucket_provider.py (new get_pr_base_ref added but no get_pr_file_content), github_provider.py (no get_pr_file_content), bitbucket_server_provider.py and gitea_provider.py DO implement get_pr_file_content; gitlab_provider.py doesn't. The PRReviewer._get_review_rules calls self.git_provider.get_pr_file_content(rule_path, ref) and wraps with try/except, but if a provider lacks that method the attribute lookup will raise AttributeError. There is no interface enforcement in git_provider base for get_pr_file_content (abstract method absent) so this will fail at runtime for providers without get_pr_file_content.
  • convert_to_markdown_v2: mutating the input
    • File: pr_agent/algo/utils.py, convert_to_markdown_v2
    • At top of function the code does: todo_summary = output_data['review'].pop('todo_summary', '') — this mutates the passed-in output_data dict. Tests may not reveal it, but mutation is undesirable. The PR already uses pop, but added structured fields may be similarly affected if input is reused elsewhere.
  • String capitalization / formatting minor inconsistency
    • File: pr_agent/algo/utils.py
    • The code uses key.replace('', ' ').capitalize() to form key_nice but later checks "risk level" in key_nice.lower() and then capitalizes risk_value via .capitalize() which only capitalizes first letter but will leave underscore variants sanitized earlier; tests expect "Safe to merge" vs "Safe to merge" – current logic mostly fine but subtle: merge_recommendation value may be "safe_to_merge" and replacement uses .replace("", " ") then .capitalize(), which yields "Safe to merge" — OK. Just noting fragility if values contain multiple words, but not a blocker.
  • Tests assume get_pr_file_content existence
    • File: tests/unittest/test_pr_reviewer.py
    • Tests call reviewer.git_provider.get_pr_file_content via MagicMock, but this won't catch runtime errors for providers missing that method in production.
  • Logging of sensitive content
    • File: pr_agent/tools/pr_reviewer.py, lines where review_rules content is logged via get_logger().info(..., artifacts={"rule_files": loaded_rule_paths}). The rule files themselves are not logged, only paths, so OK. But earlier, loaded_rules are concatenated and then clipped and returned — ensure that injecting repository content into prompts is acceptable per security/privacy policy. This is a design note rather than code bug.
  • Minor lint/style: stray import spacing
    • File: pr_agent/algo/utils.py: removed a trailing blank line at top; not an issue but noticed.
  1. Suggestions
  • Add get_pr_file_content to the git_provider abstract base class (git_provider.py) with a default implementation that raises NotImplementedError (or returns ""), and implement for all providers used in CI (GitHub, GitLab, Bitbucket, Bitbucket Server, Gitea). Currently only some providers implement it; this will avoid AttributeError at runtime. Example: define abstract def get_pr_file_content(self, file_path: str, branch: str) -> str and implement per provider by calling existing file fetch methods. At minimum, add a defensive getattr check in PRReviewer._get_review_rules to skip if method missing.
  • Normalize get_pr_base_ref behavior across providers: return branch name when possible (ref/displayId/target_branch) and fallback to commit SHA only if necessary. Document expected type (branch name vs commit SHA) and adjust get_pr_file_content implementations accordingly.
  • Avoid mutating input dicts in convert_to_markdown_v2: instead of pop('todo_summary') use .get(...). There may be existing behavior relying on pop, but safer to copy input or avoid mutation.
  • Add a unit test that instantiates PRReviewer and ensures _get_review_rules gracefully handles providers that do not implement get_pr_file_content (e.g., using a MagicMock that lacks the method). Also add tests that exercise real provider implementations for get_pr_file_content or add mocks in integration tests.
  • Ensure that clipping tokens uses the same token counting mechanism used elsewhere; clip_tokens is used but confirm it expects token count int vs characters. Tests partially cover this.
  • Consider limiting size of review_rules per file before concatenation to ensure a single very large file doesn't consume tokens; current code clips after concatenation, which is fine, but you might prefer per-file clipping and include an index of truncated files for audit.
  • Log more context when get_pr_file_content fails (include rule_path) to help debugging.
  • In PRReviewer.init, review_rules is fetched before add_ai_metadata_to_diff_files; this is fine but mention ordering: review rules are based on base ref not changed by auto metadata.
  • Small cosmetic: the persistent comment header removed emoji "🔍" in publish_persistent_comment call (line changed). If that was intentional, OK; if not, preserve previous emoji.
  1. Rating: 7/10
  • Justification: The feature is useful and implemented in a cohesive way (templating flags, provider helpers, markdown rendering, tests). However, there are gaps in the provider API (get_pr_file_content missing in some providers / base class) that would cause runtime errors for some providers. Also mutable input in convert_to_markdown_v2 is a minor problem. With the suggestions implemented (add abstract method or defensive check, normalize base ref behavior, avoid dict mutation, and add tests), this would be merge-ready.
  1. Confidence: High
  • I reviewed all changed files and tests in the diff and the issues are straightforward to reproduce from reading the code.

🔵 DeepSeek V4 Flash

Summary

This PR enhances the PR‑Agent review tool by adding structured output fields (risk_level, merge_recommendation, review_priority_files) and an optional mechanism to inject repository‑specific review rules (from configurable Markdown files) into the review prompt. The approach is sound: the new fields are conditionally enabled via TOML settings and the rule‑loading logic is optional and backward‑compatible.

Issues Found

  • Missing get_pr_file_content in several Git providers
    pr_agent/tools/pr_reviewer.py_get_review_rules() calls self.git_provider.get_pr_file_content(rule_path, ref). Only bitbucket_server_provider and gitea_provider implement this method. For GitHub, GitLab, Bitbucket Cloud, and any future provider, the call will raise AttributeError, which is caught by a generic except Exception and silently skipped. No log entry is produced, so rule loading fails invisibly. This makes the feature non‑functional on most platforms without any indication.
    Affected files:

    • pr_agent/git_providers/github_provider.py
    • pr_agent/git_providers/gitlab_provider.py
    • pr_agent/git_providers/bitbucket_provider.py
    • pr_agent/git_providers/git_provider.py (base class)
  • Possible missing clip_tokens function
    The new import from pr_agent.algo.utils import ... clip_tokens is not accompanied by any visible addition to pr_agent/algo/utils.py in this diff. If clip_tokens does not already exist in the codebase, this will cause an import error at runtime. The diff cannot confirm its presence.

  • Unintended removal of the magnifying glass emoji
    In pr_agent/tools/pr_reviewer.py line 238, initial_header=f"{PRReviewHeader.REGULAR.value} 🔍" was changed to initial_header=f"{PRReviewHeader.REGULAR.value}". This eliminates a decorative emoji that may have been intentional.

Suggestions

  • Implement get_pr_file_content in all major providers or provide a sensible default in the base GitProvider class that returns an empty string and logs a warning. This would avoid silent failures and make the feature uniformly accessible.
  • Confirm that clip_tokens exists and, if not, add a utility function that cuts a string to a maximum token count using TokenHandler.
  • Restore the 🔍 emoji unless removal was deliberate.
  • Add a log entry when get_pr_file_content is not implemented on a provider, so that operators are aware that review rules cannot be loaded.
  • Add integration tests that simulate a provider without get_pr_file_content to verify that rule loading degrades gracefully.
  • Consider extracting the rule‑loading logic into a separate helper class to keep PRReviewer.__init__ concise.

Rating

7/10 – The feature design is good and the provided tests cover the core behaviour, but the missing provider implementations and a potential missing utility function introduce reliability risks that should be addressed before merging.

Confidence

Medium – The diff clearly shows the intention and most of the code, but the absence of other provider changes for get_pr_file_content and the uncertainty about clip_tokens leave open questions that cannot be fully resolved from this diff alone.


🤖 Review generated via ensemble of 3 models (DeepSeek V4 Flash, Gemini 3 Flash, GPT-5 Mini)

@CzechExplorer
Copy link
Copy Markdown

📋 Ensemble PR Review

PR: Add review risk assessment and optional repository review rules
URL: #2391
Author: @xiaodeng666a


🔵 DeepSeek V4 Flash

❌ Error: 402 Client Error: Payment Required for url: https://api.poe.com/v1/chat/completions


🟠 Gemini 3.1 Flash

Summary

This PR enhances the PRReviewer tool by introducing a structured risk assessment (risk level, merge recommendation, and priority files) and the ability to load repository-specific review rules from markdown files. These changes allow for more actionable review summaries and enable teams to enforce custom coding standards or focus areas via configuration files stored within the repository.

Issues Found

1. Potential Logic Error: last_key in _prepare_pr_review

  • File: pr_agent/tools/pr_reviewer.py (Lines 300-318)
  • Issue: The last_key is hardcoded to "security_concerns". However, the PR adds new fields like risk_level, merge_recommendation, and review_priority_files.
  • Impact: If the LLM places these new fields after security_concerns in the YAML output, the load_yaml function might truncate them or fail to parse them correctly depending on how the regex/logic in load_yaml handles the "last key" boundary.
  • Recommendation: Update last_key to the last possible field in the schema, or ensure load_yaml is robust to the new ordering.

2. Inconsistent Implementation of get_pr_base_ref

  • File: pr_agent/git_providers/bitbucket_provider.py (Line 492) and others.
  • Issue: While GithubProvider and GitlabProvider implementations look correct, the BitbucketProvider uses self.pr.destination_branch. In some Bitbucket API versions, this might be a name rather than a commit SHA.
  • Impact: If get_pr_file_content expects a SHA but receives a branch name that has moved, it might fetch the wrong version of the rules, though usually, the branch name is acceptable for "base".

3. Missing get_pr_file_content in several providers

  • File: pr_agent/git_providers/github_provider.py, pr_agent/git_providers/gitlab_provider.py, pr_agent/git_providers/bitbucket_provider.py.
  • Issue: The PRReviewer._get_review_rules method calls self.git_provider.get_pr_file_content(...). This method was added to BitbucketServerProvider and GiteaProvider in this PR, but it is not implemented in the main GitHub, GitLab, or Bitbucket Cloud providers.
  • Impact: The "Review Rules" feature will likely crash or raise an AttributeError when running on GitHub/GitLab because the method is missing from those classes.

Suggestions

  1. Implement get_pr_file_content across all providers: Ensure that github_provider.py, gitlab_provider.py, and bitbucket_provider.py all implement the new method defined in the base class (or ensure the base class has a working default).
  2. Refine YAML Parsing: In pr_agent/tools/pr_reviewer.py, instead of manually updating keys_fix_yaml every time a field is added, consider a more dynamic way to generate the fix list from the Pydantic model or the settings.
  3. Default Values in configuration.toml: The new require_... settings are set to false by default. This is good for backward compatibility, but you might want to provide a "recommended" preset in the documentation so users know these features exist.
  4. Token Clipping Warning: In _get_review_rules, if review_rules is clipped, it might be helpful to append a small note like ... [Rules truncated due to token limit] so the LLM knows it's looking at a partial list.

Rating: 7/10

The PR is well-structured and the logic for rendering the new markdown fields is solid. However, it is currently not merge-ready because it introduces a dependency on get_pr_file_content which is missing from the most commonly used git providers (GitHub/GitLab), which will cause runtime errors for most users.

Confidence: High

The code review covers the logic flow from configuration to prompt generation and rendering. The missing method implementation is a critical path for the "Review Rules" feature.


🔷 GPT-5 Mini

  1. Summary
  • This PR adds structured review fields (risk_level, merge_recommendation, review_priority_files) to the review output, renders them in convert_to_markdown_v2, and wires them into the prompt/template and settings flags. It also implements optional repository review rules: configurable file paths read from the repository base ref, clipped by token budget, and injected into the review prompt. Overall approach is reasonable and keeps backward-compatibility when rules are absent.
  1. Issues Found
  • Potential missing abstract method implementation expectation:

    • File: pr_agent/git_providers/git_provider.py
      • Lines added: get_pr_base_ref logs and returns "", but the base class didn't mark it abstract; callers rely on provider implementations (good), but the warning may be noisy. Not a correctness bug but worth noting.
  • Inconsistent base ref semantics and possible wrong ref usage:

    • Multiple providers implement get_pr_base_ref returning sha or ref (github returns base.sha or base.ref; bitbucket_server uses latestCommit or displayId). However the PR reviewer _get_review_rules uses that ref as the ref when reading files via provider.get_pr_file_content(rule_path, ref). Many git providers' get_file/get_pr_file_content implementations expect branch name (ref like branch name) while some get_pr_base_ref return commit SHA (e.g., GitHub base.sha). Using sha vs branch may be OK, but it's brittle across providers and some providers implement get_pr_file_content while others don't (see bitbucket_provider, gitea_provider, bitbucket_server_provider add get_pr_file_content). If a provider doesn't implement get_pr_file_content, _get_review_rules will call the provider's get_pr_file_content and raise AttributeError. Specific places:
      • pr_agent/git_providers/bitbucket_provider.py: get_pr_file_content was NOT added (only get_pr_base_ref added). So for Bitbucket (cloud?) provider, _get_review_rules will call get_pr_file_content and fail unless get_pr_file_content exists on that provider. In the diff, only bitbucket_server_provider and gitea_provider and maybe others have get_pr_file_content; bitbucket_provider did not add a get_pr_file_content implementation. So this is a likely runtime AttributeError.
  • Logging of sensitive/untrusted content:

    • pr_agent/tools/pr_reviewer.py:
      • Lines: get_logger().info("Loaded review rules for this PR", artifacts={"rule_files": loaded_rule_paths}) — ok.
      • But earlier it logs "Could not resolve a trusted base ref for review rules" — fine.
    • Minor security: when reading review rules, content is concatenated and injected into prompt. This can be attacker-controlled if review rules are in the repo — this is intended but should be noted as a security consideration (prompt injection). There's no sanitization; the README/PR notes should highlight this. No code-level block but worth calling out.
  • convert_to_markdown_v2 logic duplication and fragile matching:

    • File: pr_agent/algo/utils.py
      • The new branches check for "risk level", "merge recommendation", "review priority files" by checking key_nice.lower(). That logic depends on key replacement and capitalization earlier (key.replace('_', ' ').capitalize()). This may mismatch if keys come with different capitalization / underscores. For example key 'risk_level' becomes 'Risk level' -> lower -> 'risk level' matches; good. But earlier emojis mapping uses "Risk level" as key in the emojis dict; that is fine. However the code inserted duplicates some similar patterns and order matters. This is manageable but a bit verbose.
  • Tests might not cover error paths or provider missing methods:

    • tests/unittest/test_pr_reviewer.py stubs provider.get_pr_file_content and get_pr_base_ref, but there is no test that ensures missing get_pr_file_content on provider is handled gracefully. Given the current code, missing method will raise AttributeError.
  • Minor style: In pr_agent/tools/pr_reviewer.py, persistent_comment header removed the magnifying emoji (change at lines where initial_header set to f"{PRReviewHeader.REGULAR.value}", previously included " 🔍"). This is a behavior change not mentioned in PR description — maybe intentional but should be called out.

  1. Suggestions
  • Defensive calls for provider.get_pr_file_content:
    • Update PRReviewer._get_review_rules to use getattr(self.git_provider, "get_pr_file_content", None) and skip paths if method missing, logging a warning. This will avoid AttributeError for providers that don't implement file retrieval by ref.
  • Standardize get_pr_base_ref semantics:
    • Document whether get_pr_base_ref must return a branch name or commit SHA and make implementations consistent. Consider adding a helper in the base class to turn base ref into a branch/ref that get_file/get_file_content accept, or add a get_file_from_ref(file_path, ref) abstract method in git_provider that providers implement consistently.
  • Sanitize / limit injected review rules:
    • There is token clipping (clip_tokens) which is good. Also consider removing YAML front matter or long code blocks, or explicitly prefixing the injected content with "Repository/team review rules (do not execute):" and instruct the model to treat it as guidance (template already partly does). Add a comment in PR about prompt-injection risk.
  • Improve convert_to_markdown_v2 logic:
    • Rather than string matching on key_nice, consider mapping raw key names to renderers (e.g., check original key like 'risk_level' in key.lower()) to be less fragile.
  • Tests:
    • Add tests that simulate a git provider missing get_pr_file_content and ensure _get_review_rules returns "" (or at least doesn't raise).
    • Add coverage for the situation where get_pr_base_ref returns a sha vs branch; maybe a mock provider that raises when given sha vs branch isn't possible in unit tests, but add tests to ensure get_pr_file_content called with the ref returned.
    • Add tests for clip_tokens being applied when max_review_rules_tokens > 0.
    • Add test that PRReviewer.init still sets review_rules in the constructed data mapping (maybe integration test).
  • Minor: Reintroduce or justify removal of "🔍" emoji from persistent comment header, or document the change.
  • Settings defaults:
    • In configuration.toml new flags default to false; ensure documentation is updated. Consider grouping related flags into a nested block.
  1. Rating: 7/10
  • Reasoning: The feature additions are clear and fairly well-implemented, with tests for the markdown rendering and review rules loading logic. However, there is a practical robustness issue: not all providers implement get_pr_file_content, and _get_review_rules will call it unconditionally, causing runtime errors for some providers (e.g., bitbucket_provider). Also some minor behavior changes (persistent comment header emoji) and prompt-injection considerations should be addressed before merging.
  1. Confidence: High
  • I inspected the diff for function additions/usage and identified concrete mismatches and missing defensive coding; these are straightforward to reproduce and reason about.

🤖 Review generated via ensemble of 3 models (DeepSeek V4 Flash, Gemini 3 Flash, GPT-5 Mini)

@CzechExplorer
Copy link
Copy Markdown

📋 Ensemble PR Review

PR: Add review risk assessment and optional repository review rules
URL: #2391
Author: @xiaodeng666a


🔵 DeepSeek V4 Flash

❌ Error: 402 Client Error: Payment Required for url: https://api.poe.com/v1/chat/completions


🟠 Gemini 3.1 Flash

Summary

This PR enhances the pr-agent review process by introducing structured risk assessment fields (risk_level, merge_recommendation, review_priority_files) and a mechanism to inject repository-specific review rules from markdown files (e.g., .github/review_rules.md). The approach is sound, maintaining backward compatibility while providing more actionable insights and customization for development teams.


Issues Found

1. Logic Error: Missing get_pr_file_content in GithubProvider and GitlabProvider

  • Files: pr_agent/git_providers/github_provider.py, pr_agent/git_providers/gitlab_provider.py
  • Issue: The PRReviewer._get_review_rules method calls self.git_provider.get_pr_file_content(rule_path, ref). While this method was added to BitbucketServerProvider and GiteaProvider, it is missing from the GitHub and GitLab providers.
  • Impact: On GitHub and GitLab, the logic will likely fall back to the base class (if defined) or fail, preventing the "Review Rules" feature from working on the two most popular platforms.

2. Logic Error: last_key in _prepare_pr_review might truncate new fields

  • File: pr_agent/tools/pr_reviewer.py, line 300
  • Issue: The load_yaml call uses last_key = "security_concerns".
  • Impact: If the LLM places the new fields (risk_level, merge_recommendation, etc.) after security_concerns in the YAML output, they might be truncated or ignored by the parser depending on how load_yaml is implemented. It is safer to update last_key to the last possible field in the schema.

3. Redundant Code / Cleanup

  • File: pr_agent/tools/pr_reviewer.py, line 356
  • Issue: There is an extra newline and a check if markdown_text == None or len(markdown_text) == 0: markdown_text = "".
  • Impact: Minor readability issue. convert_to_markdown_v2 usually returns a string; this check is largely redundant but harmless.

Suggestions

  1. Implement get_pr_file_content for all providers: Ensure github_provider.py and gitlab_provider.py implement the file retrieval logic so the feature is cross-platform.
  2. Update last_key in YAML parsing: In pr_agent/tools/pr_reviewer.py, update last_key to include the new fields or ensure the Pydantic-like schema in the prompt forces a specific order that matches the parser.
  3. Refine risk_level logic in Markdown: In pr_agent/algo/utils.py, the code uses str(value).strip().lower().replace("_", " "). Consider adding a mapping for colors or emojis based on the level (e.g., High = 🔴, Low = 🟢) to make the "Risk Level" more visually scannable.
  4. Add Integration Test: While unit tests were added for the markdown conversion, an integration test ensuring the review_rules are actually injected into the final prompt would be beneficial.

Rating: 7/10

The PR is well-structured and the feature is highly valuable. However, it is not yet "merge-ready" because the core functionality (reading review rules) appears to be unimplemented for GitHub and GitLab, which are primary targets for this tool.


Confidence: High

I have reviewed the logic for file retrieval across the different provider implementations and the prompt template changes.


🔷 GPT-5 Mini

  1. Summary
  • This PR adds structured review outputs (risk_level, merge_recommendation, review_priority_files) to the reviewer schema and markdown renderer, and implements optional repository-level review rules that can be loaded from configurable markdown files in the PR base ref. The approach is straightforward and mostly sound: extend the prompt/schema, render new fields in convert_to_markdown_v2, add settings flags, and implement review-rule loading in PRReviewer using git_provider helpers.
  1. Issues Found
  • Potential silent failures when reading review rules (pr_agent/tools/pr_reviewer.py)
    • File: pr_agent/tools/pr_reviewer.py, method _get_review_rules()
    • Problem: The code calls self.git_provider.get_pr_file_content(rule_path, ref) inside a try/except that catches Exception but continues silently; while that is acceptable for optional files, there is no differentiation between "file not found" vs "API error". This may be acceptable but reduces observability. (Lines: the try/except loop in _get_review_rules).
  • Missing or inconsistent git_provider implementations for get_pr_file_content/get_pr_base_ref
    • Files: pr_agent/git_providers/git_provider.py (base class) adds get_pr_base_ref stub that returns "", and bitbucket_server_provider.py, gitea_provider.py, github_provider.py, gitlab_provider.py add get_pr_base_ref implementations — but:
      • bitbucket_server_provider.get_pr_base_ref returns latestCommit or displayId; using latestCommit (a commit SHA) may be fine, but for file retrieval you often need the branch name or commit SHA — behavior differs across providers. There's risk that some providers return a displayId (branch name) and others a commit SHA; get_pr_file_content implementations (where present) must accept that. Make sure repo file retrieval functions accept either form.
    • Only some providers implement get_pr_file_content: gitea_provider and bitbucket_server_provider add get_pr_file_content, but github_provider, gitlab_provider, bitbucket_provider (cloud) do not. PRReviewer calls self.git_provider.get_pr_file_content(rule_path, ref) for all providers; if the provider doesn't implement it, it will raise AttributeError. The abstract GitProvider did not add an abstract get_pr_file_content method. (Relevant: pr_agent/tools/pr_reviewer.py line where get_pr_file_content invoked; pr_agent/git_providers/git_provider.py does not define get_pr_file_content.)
  • Minor logging/information leakage
    • File: pr_agent/tools/pr_reviewer.py around run/_prepare_pr_review: logs get_logger().info(f"Risk level: {data.get('review', {}).get('risk_level')}") etc. If the review contains sensitive content, logging may leak content into logs/artifacts. Consider redaction or using debug level.
  • convert_to_markdown_v2: capitalization and matching rules
    • File: pr_agent/algo/utils.py convert_to_markdown_v2
    • Problem: New keys are added to emojis mapping as "Risk level", "Merge recommendation", "Review priority files" while the later code checks key_nice = key.replace('_',' ').capitalize() then looks for substrings like "risk level" in lowercase. That mostly works, but there are earlier checks that skip empty values by comparing key.lower() not in [...] and they add 'review_priority_files' to that skip list. This means if review_priority_files is empty it will be allowed (they intentionally included it) — okay. But the code uses several different casing/substring checks which are brittle and rely on consistent key names; acceptable but could be more robust by normalizing key names once.
  • Tests may not run for providers lacking get_pr_file_content
    • File: tests/unittest/test_pr_reviewer.py
    • The tests stub reviewer.git_provider.get_pr_file_content, which is fine for unit tests, but nothing enforces that real providers implement the method. This gap can cause runtime errors when review rules are enabled against providers lacking that method.
  • Small formatting regression
    • File: pr_agent/tools/pr_reviewer.py: in run(), initial_header changed from f"{PRReviewHeader.REGULAR.value} 🔍" to f"{PRReviewHeader.REGULAR.value}" — removal of magnifying-glass emoji seems unrelated to PR scope (maybe intentional), but this is a behavioral change. (Lines around publishing persistent comment.)
  1. Suggestions
  • Add get_pr_file_content to the base git_provider class as an abstract method (or at least a default implementation that attempts to call existing file retrieval functions) so callers can rely on its presence. Update all providers to implement it or raise a clear NotImplementedError with a helpful message. This prevents AttributeError at runtime.
  • Normalize ref semantics across providers and document whether get_pr_base_ref returns branch name or commit SHA; make get_pr_file_content accept either. Consider renaming to get_pr_base_ref_or_sha for clarity or document in base class.
  • Improve observability in _get_review_rules: log the specific error when file retrieval fails (at debug) and when a 404/not found occurs, so maintainers can distinguish misconfiguration vs absent file.
  • Avoid broad except Exception: either catch expected exceptions (APIError, NotFound) or at least log them at debug level. Currently exceptions are swallowed silently in a couple of places (also bitbucket_server_provider.get_pr_file_content and gitea_provider.get_pr_file_content do catch Exception and return ""), which is OK for optional rules but make sure logs exist.
  • Make convert_to_markdown_v2 key handling more robust: normalize keys once (e.g., key_norm = key.strip().lower()) and switch on that, instead of relying on multiple substring checks. That will reduce brittleness.
  • Tests:
    • Add integration-like tests or unit tests that exercise a provider missing get_pr_file_content to assert _get_review_rules handles it gracefully (returns ""), to prevent regressions.
    • Add tests to ensure clip_tokens truncation happens when max_review_rules_tokens is set (e.g., set to small number and verify returned string shorter).
    • Add a test that verifies PRReviewer includes review_rules in the prompt context (maybe mock get_settings and ensure template rendering receives review_rules).
  • Schema validation: the new prompt fields require exact string values (e.g., risk_level must be low/medium/high, merge_recommendation must be one of safe_to_merge/merge_with_caution/changes_required). Consider adding stricter validation code after parsing (e.g., coerce or default unknown values) and/or add unit tests to ensure parse failures are handled cleanly.
  • Logging sensitivity: use debug for logging the actual review field values, or sanitize or avoid logging arbitrary text if logs are sent to external sinks.
  • Re-evaluate the removed emoji in persistent comment header (intentional?) and restore if necessary.
  • Minor: in convert_to_markdown_v2, when rendering merge_recommendation the tests expect "Merge with caution" but code capitalizes the entire phrase; consider mapping known tokens (safe_to_merge -> "Safe to merge") to provide nicer display consistently.
  1. Rating: 7/10
  • Reason: The feature is well thought-out, the implementation is mostly correct, and unit tests were added for key parts. The main issues are interface gaps (get_pr_file_content not enforced in base provider), inconsistent provider implementations, and some logging/observability concerns. Those problems are fixable with a small follow-up. Once providers are made consistent and a couple of edge-case tests added, this is merge-ready.
  1. Confidence: High
  • I reviewed the diff across the touched files and the testing additions; the issues noted are concrete and dominated by the provider interface and logging/observability.

🤖 Review generated via ensemble of 3 models (DeepSeek V4 Flash, Gemini 3 Flash, GPT-5 Mini)

@CzechExplorer
Copy link
Copy Markdown

Error running review: Traceback (most recent call last):
File "/home/homelab/ensemble-review/ensemble_review.py", line 313, in
main()
File "/home/homelab/ensemble-review/ensemble_review.py", line 295, in main
prompt = build_review_prompt(pr_info, diff, pr_url)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/homelab/ensemble-review/ensemble_review.py", line 168, in build_review_prompt
{REVIEW_PROMPT}
^^^^^^^^^^^^^
NameError: name 'REVIEW_PROMPT' is not defined. Did you mean: 'REVIEW_PROMPT_FULL'?

@CzechExplorer
Copy link
Copy Markdown

Test comment from webhook via API

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants