Skip to content

Add review risk assessment and optional repository review rules#2391

Open
xiaodeng666a wants to merge 17 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 17 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 (2) 📘 Rule violations (5)

Grey Divider


Action required

1. Review rules lack tests 📘 Rule violation ☼ Reliability ⭐ New
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


2. .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


3. 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


View more (17)
4. 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


5. 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


6. 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


7. 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


8. 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


9. 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


10. 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


11. 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


12. 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


13. Extra blank lines in imports 📘 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


14. 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


15. 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


16. 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


17. Unescaped HTML breaks output ✓ Resolved 🐞 Bug ≡ Correctness
Description
convert_to_markdown_v2() interpolates risk_level, merge_recommendation, and
review_priority_files values directly into HTML (...) without escaping, so values containing
&, `` can corrupt the table and hide or distort the review content. This is especially relevant
for review_priority_files because file paths can legitimately contain HTML-sensitive characters
and the value may also be model-generated.
Code

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

+        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'
+            if gfm_supported:
+                markdown_text += f"<tr><td><strong>Merge recommendation</strong>: {recommendation_display}</td></tr>\n"
+            else:
+                markdown_text += f"### Merge recommendation: {recommendation_display}\n\n"
+        elif 'review priority files' in key_nice.lower():
+            if gfm_supported:
+                markdown_text += "<tr><td>"
+                if not value:
+                    markdown_text += "<strong>Priority files</strong>: None"
+                else:
+                    markdown_text += "<strong>Priority files</strong><br><br>"
+                    for priority_file in value:
+                        markdown_text += f"- {priority_file}<br>"
+                markdown_text += "</td></tr>\n"
Evidence
The function explicitly emits a ` when gfm_supported` is true and then writes the new fields into
HTML table cells using f-strings with no escaping, which makes the rendered output sensitive to
special characters in the values.

pr_agent/algo/utils.py[169-176]
pr_agent/algo/utils.py[239-270]

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()` generates HTML table rows for the newly-added fields (`risk_level`, `merge_recommendation`, `review_priority_files`) and interpolates raw strings into `<td>` content. If those strings contain HTML-sensitive characters (e.g., `&`, `<`, `>`), the rendered table can break and obscure parts of the review.
## Issue Context
The GFM path uses raw HTML (`<table>`, `<tr>`, `<td>`). The new code paths write unescaped user/model-provided strings into these HTML fragments.
## Fix Focus Areas
- pr_agent/algo/utils.py[169-176]
- pr_agent/algo/utils.py[239-270]
### Implementation notes
- Use `html.escape(...)` (stdlib) on `risk_display`, `recommendation_display`, and each `priority_file` before interpolating into HTML.
- Keep the non-GFM markdown path unchanged (or consider also normalizing/escaping there if you later add HTML).

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


18. YAML fallback breaks file list ✓ Resolved 🐞 Bug ☼ Reliability
Description
PRReviewer._prepare_pr_review() adds review_priority_files: to keys_fix_yaml, but
try_fix_yaml() treats every keys_fix_yaml entry as a multi-line scalar and rewrites
review_priority_files: into a | block during recovery. This can invalidate the list structure
shown in the prompt example and cause YAML parsing to fail or yield the wrong type, preventing
/review from publishing correctly.
Code

pr_agent/tools/pr_reviewer.py[R287-292]

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:", "review_priority_files:", "security_concerns:", "key_issues_to_review:",
                    "relevant_file:", "relevant_line:", "suggestion:"],
     first_key=first_key, last_key=last_key)
Evidence
The PR adds review_priority_files: to the set of keys that YAML recovery rewrites into block
scalars, while the YAML recovery implementation unconditionally applies that rewrite to any matching
line, and the prompt example emits review_priority_files as a list under the
review_priority_files: key line.

pr_agent/tools/pr_reviewer.py[287-292]
pr_agent/algo/utils.py[784-799]
pr_agent/algo/utils.py[803-821]
pr_agent/settings/pr_reviewer_prompts.toml[194-198]

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_priority_files:` is a list field, but it is included in `keys_fix_yaml`, which is used by `try_fix_yaml()` to convert matching keys into block scalars (`|`). During YAML recovery this can corrupt the expected list structure and make parsing brittle.
## Issue Context
- `load_yaml()` calls `try_fix_yaml()` on parse failures.
- `try_fix_yaml()`’s first fallback rewrites any line containing a `keys_fix_yaml` key into a block scalar, regardless of whether that key is supposed to be a string or a list.
- The prompt’s example output emits `review_priority_files:` followed by a YAML list, which is exactly the pattern this fallback would rewrite incorrectly.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[287-292]
- pr_agent/algo/utils.py[784-799]
- pr_agent/algo/utils.py[803-821]
- pr_agent/settings/pr_reviewer_prompts.toml[194-198]
## Suggested fix
- Remove `"review_priority_files:"` from `keys_fix_yaml` in `_prepare_pr_review()`.
- (Optional, more robust) Update `try_fix_yaml()` to only apply the `|` transformation to keys that are defined as multi-line strings, or add a guard that skips keys whose value is a YAML sequence/mapping (e.g., if the next non-empty line starts with `- `).

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


19. Misindented warning() continuation ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
The get_logger().warning(...) call has misindented continuation lines, which is likely to fail
Ruff/PEP8 linting and break CI. This violates the repository’s required Ruff style consistency for
Python changes.
Code

pr_agent/tools/pr_reviewer.py[R162-165]

+            get_logger().warning(
+            "Invalid max_review_rules_tokens value; skipping token clipping",
+            artifacts={"max_review_rules_tokens": max_tokens},
+            )
Evidence
PR Compliance ID 9 requires Python changes to conform to Ruff style. The added
get_logger().warning( call has its argument lines aligned incorrectly (continuation indentation),
which is a typical Ruff formatting violation.

AGENTS.md
pr_agent/tools/pr_reviewer.py[162-165]

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 newly added `get_logger().warning(...)` call uses incorrect continuation indentation for its arguments. Ruff/PEP8 commonly flags this (continuation line under-indented), which can fail linting/CI.
## Issue Context
This code is inside `PRReviewer._get_review_rules()` and was introduced in this PR.
## Fix Focus Areas
- pr_agent/tools/pr_reviewer.py[162-165]

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


20. max_tokens always reset to 0 ✓ Resolved 📘 Rule violation ≡ Correctness
Description
PRReviewer._get_review_rules() unconditionally resets max_tokens to 0 outside the
try/except, so token clipping is effectively disabled even when max_review_rules_tokens is a
valid configured integer. This allows oversized review-rules content into the prompt, increasing
token pressure and potentially degrading review quality or truncating other prompt parts,
undermining the intended safety limit.
Code

[pr_agent/too...

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/algo/utils.py Outdated
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 15, 2026

Persistent review updated to latest commit 666a265

Comment on lines 234 to 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, )
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. Persistent header string mismatch 🐞 Bug ≡ Correctness

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.
Agent Prompt
## 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

@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 8c93ceb

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 15d8940

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 20a256d

@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 b71e7be

Comment thread pr_agent/tools/pr_reviewer.py Outdated
@@ -1,6 +1,9 @@
import copy
import copy
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. Bom character in import copy 📘 Rule violation ⚙ Maintainability

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.
Agent Prompt
## 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

Comment thread pr_agent/settings/configuration.toml 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 b97e833

Comment on lines +84 to +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
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. .pr_agent.toml missing new keys 📘 Rule violation ⚙ Maintainability

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.
Agent Prompt
## 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

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 983d227

Comment on lines +128 to +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
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. Review rules lack tests 📘 Rule violation ☼ Reliability

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.
Agent Prompt
## 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

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.

1 participant