Skip to content

fix(azure): do not resolve a comment thread after a swallowed tool failure - #3722

Merged
IsmaelMartinez merged 2 commits into
The-PR-Agent:mainfrom
Lesereingrape:fix/azure-comment-thread-status
Sep 29, 2026
Merged

IsmaelMartinez merged 2 commits into
The-PR-Agent:mainfrom
Lesereingrape:fix/azure-comment-thread-status

Conversation

@Lesereingrape

@Lesereingrape Lesereingrape commented Sep 27, 2026 •

Copy link
Copy Markdown

What happens

handle_request_comment() resolves the discussion thread and deletes the progress comment whenever PRAgent.handle_request() returns True:

handled = await agent.handle_request(url, body, notify=...)
if handled and not is_question:
    provider.set_thread_status(thread_id, "closed")

With the default propagate_tool_errors = false, a tool that fails internally calls record_command_failure() and returns normally anyway (pr_agent/tools/pr_reviewer.py:458-464, same shape in pr_description, pr_code_suggestions, pr_add_docs, pr_generate_labels, pr_help_message). So a /review whose model call errored still gets its thread resolved and its progress comment removed — the author sees a closed discussion and no review, with nothing marking the command as failed.

The GitHub comment path already avoids this by demanding an honest verdict (pr_agent/servers/github_app.py:164-169, the fix for #3170). The other comment-triggered servers (gitlab_webhook.py:72, bitbucket_app.py:367) discard the return value, so Azure is the remaining path that acts on that boolean without asking for a truthful one.

The second symptom, which only shows up once the verdict becomes honest: notify() posts the On it! ⏳ reply before the command runs and records it as a temporary comment (azuredevops_provider.py:1674-1687), but the cleanup is gated on the same boolean — if handled: provider.remove_initial_comment(). A command that fails therefore leaves that reply in the thread with nothing to replace it. The tools themselves take their temporary comments back in finally: blocks (pr_questions.py:93-99, pr_update_changelog.py:164-170, pr_add_docs.py:86-92), so the outcome-independent cleanup is the existing convention here.

Change

  • pr_agent/servers/azuredevops_server_webhook.py — handle_request_comment() passes propagate_tool_errors=True, matching the GitHub comment path. The keyword is restored to its previous value by _run_command() afterwards, so repository and command settings are unaffected; a swallowed tool error now surfaces as False and the thread stays open for a retry.
  • same function — the temporary reply is removed on every run, so a failed command leaves no orphaned On it! ⏳ behind. Only closing the thread stays conditional on the outcome.
  • tests/unittest/test_azure_devops_webhook_comments.py — two tests drive the comment endpoint through the real PRAgent with a stand-in tool that reproduces the documented swallow-or-raise behaviour: the failing run must keep the thread open while still removing the progress reply, and a clean run must still close the thread.

Fixes #3721

Validation

  • PYTHONPATH=. .venv/Scripts/python.exe -m pytest tests/unittest/test_azure_devops_webhook_comments.py -q — 30 passed
  • RED for the propagation change, with only that production line reverted — 1 failed, 29 passed: AssertionError: Expected 'set_thread_status' to not have been called. Called 1 times.
  • RED for the cleanup change, with only that guard reverted — 1 failed, 29 passed: AssertionError: Expected 'remove_initial_comment' to have been called once. Called 0 times.
  • neighbours: test_run_status_acknowledgment.py, test_azure_devops_provider.py, test_azure_devops_webhook_auth.py together with this file — 225 passed; the wider -k "azure or webhook" selection over tests/unittest — 1887 passed
  • ruff check on both changed files — All checks passed!
  • Not covered: a live Azure DevOps Server with a real model call. The tests fake the tool and the provider; the agent, the settings propagation and the handler are the real ones.

AI assistance was used for this change.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Keep Azure comment threads open when tool commands fail

🐞 Bug fix 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Require tool errors to propagate so failed Azure comment commands do not appear successful.
• Leave failed threads open and retain their progress comments; successful reviews still close
 threads.
• Add regression tests through the real agent for failed and successful commands.
Diagram

graph TD
  A["Azure comment"] --> B["Comment handler"] --> C["PRAgent request"] --> D["Tool run"] --> E{"Handled?"}
  E -->|No| F["Thread stays open"]
  E -->|Yes| G["Close thread"] --> H["Remove progress"]
Loading
High-Level Assessment

Use the existing per-request propagation option, as the GitHub comment path does. It makes the boolean reliable without changing tool contracts or permanently altering settings; a new outcome API would be disproportionate for this fix.

Files changed (2) +74 / -2

Bug fix (1) +5 / -2
azuredevops_server_webhook.pyRequest a truthful command result for Azure comments +5/-2

Request a truthful command result for Azure comments

• Passes propagate_tool_errors=True when dispatching comment commands. An internally failed tool can no longer produce the successful result that closes the thread and removes its progress comment.

pr_agent/servers/azuredevops_server_webhook.py

Tests (1) +69 / -0
test_azure_devops_webhook_comments.pyCover failed and successful review thread outcomes +69/-0

Cover failed and successful review thread outcomes

• Adds tests that exercise the webhook through the real PRAgent with stand-in tools. They verify that a failed review keeps its thread and progress comment, while a successful review still closes the thread and removes the comment.

tests/unittest/test_azure_devops_webhook_comments.py

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (4) 📎 Requirement gaps (0) 🎨 UX issues (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. A progress comment narrates cleanup 📘 Rule violation ⚙ Maintainability
Description
The new comment above provider.remove_initial_comment() describes what happened to the progress
reply instead of instructing the reader to remove it after a failed command. When someone revisits
the cleanup logic, the comment explains the scenario but does not state the handling step the
unconditional call must preserve.
Code

pr_agent/servers/azuredevops_server_webhook.py[R76-77]

+            # The progress reply was posted before the command ran, so a failed run still has to
+            # take it back; only closing the thread depends on the outcome.
Evidence
The checklist requires behavior-describing comments to use imperative phrasing. The added comment
begins The progress reply was posted and continues as a narrative description.

Rule 2694688: Docstrings and comments must use imperative phrasing
pr_agent/servers/azuredevops_server_webhook.py[76-78]

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 new progress-reply comment uses narrative phrasing rather than an instruction.
## Fix Focus Areas
- pr_agent/servers/azuredevops_server_webhook.py[76-77]
## Recommended Fix
Rewrite the comment in imperative mood, directing cleanup of the progress reply after the command regardless of its outcome.

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


2. Two async tests omit required markers 📘 Rule violation ▣ Testability
Description
test_swallowed_tool_failure_does_not_resolve_the_thread and
test_successful_command_still_resolves_the_thread are async tests without @pytest.mark.asyncio.
The project’s automatic asyncio mode lets them run, but both new tests still lack the decorators
required by the checklist.
Code

tests/unittest/test_azure_devops_webhook_comments.py[335]

+async def test_swallowed_tool_failure_does_not_resolve_the_thread(monkeypatch):
Evidence
Both added test functions are declared async and have no asyncio decorator; the checklist requires
that marker even though the repository configures automatic asyncio mode.

Rule 2982393: Decorate async pytest tests with @pytest.mark.asyncio
tests/unittest/test_azure_devops_webhook_comments.py[335-351]
pyproject.toml[188-191]

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

## Issue description
Both new async tests lack the required pytest asyncio marker.
## Fix Focus Areas
- tests/unittest/test_azure_devops_webhook_comments.py[335-351]
## Recommended Fix
Add `@pytest.mark.asyncio` immediately above each new async test.

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


3. Azure failure comment uses narration 📘 Rule violation ⚙ Maintainability
Description
The new comment above propagate_tool_errors=True describes what a failing tool does instead of
instructing the reader to request a truthful outcome. It sits beside the override that governs
whether a failed review leaves the discussion open, so a later reader gets a narrative account
rather than the intended handling instruction.
Code

pr_agent/servers/azuredevops_server_webhook.py[R70-72]

+                # A tool that fails internally returns normally while `propagate_tool_errors` is
+                # false, and then a resolved discussion thread plus a deleted progress comment would
+                # read as "review done" for a review that was never published.
Evidence
The added comment begins A tool that fails internally returns normally, a descriptive statement
about behavior rather than imperative phrasing.

Rule 2694688: Docstrings and comments must use imperative phrasing
pr_agent/servers/azuredevops_server_webhook.py[70-73]

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 new behavioral comment uses narrative phrasing rather than an instruction.
## Fix Focus Areas
- pr_agent/servers/azuredevops_server_webhook.py[70-72]
## Recommended Fix
Rewrite the comment as an imperative instruction to propagate tool failures before deciding whether to resolve the thread.

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


View medium (2)
4. Two regression docstrings use narration 📘 Rule violation ⚙ Maintainability
Description
The new test docstrings begin with Resolving the discussion and The companion guard rather than
imperative verbs. They describe the failure and success cases for the same comment-handler
regression, leaving both test descriptions outside the required phrasing convention.
Code

tests/unittest/test_azure_devops_webhook_comments.py[R336-338]

+    """Resolving the discussion and deleting the progress comment on a run that raised inside the
+    tool leaves the author a closed thread and no review, so the handler must demand an honest
+    verdict the way the GitHub comment path does."""
Evidence
Neither added test docstring starts with an imperative verb, as required for function docstrings by
the checklist.

Rule 2694688: Docstrings and comments must use imperative phrasing
tests/unittest/test_azure_devops_webhook_comments.py[335-338]
tests/unittest/test_azure_devops_webhook_comments.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
Both new test docstrings describe their cases without an imperative opening.
## Fix Focus Areas
- tests/unittest/test_azure_devops_webhook_comments.py[336-338]
- tests/unittest/test_azure_devops_webhook_comments.py[348-349]
## Recommended Fix
Start each docstring with an imperative verb describing what the test verifies.

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


5. Failed commands leave “On it” behind ✓ Resolved
Description
handle_request_comment() now receives False when a tool propagates an error, so its if handled
guard skips remove_initial_comment(). When the notification has already posted its temporary “On
it! ⏳” reply, that reply remains in the open thread after the command stops, and a later request
cannot clean it up through the previous provider instance.
Code

pr_agent/servers/azuredevops_server_webhook.py[73]

+                propagate_tool_errors=True)
Evidence
The new propagation argument makes a tool error return False; the handler then skips its only
cleanup call. Azure records the notification as a temporary comment on that provider instance, which
is created for this invocation.

pr_agent/servers/azuredevops_server_webhook.py[59-77]
pr_agent/agent/pr_agent.py[290-305]
pr_agent/git_providers/azuredevops_provider.py[1674-1688]
pr_agent/git_providers/azuredevops_provider.py[1216-1221]
pr_agent/git_providers/azuredevops_provider.py[203-219]

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 failed Azure comment command leaves its temporary “On it! ⏳” reply in the thread because cleanup is conditional on successful handling.
## Fix Focus Areas
- pr_agent/servers/azuredevops_server_webhook.py[68-77]
- tests/unittest/test_azure_devops_webhook_comments.py[335-355]
## Recommended Fix
Remove the temporary reply after the command finishes regardless of its result, while keeping thread resolution conditional on success. Update the failure test to expect progress-reply cleanup without thread resolution.

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


  • Author self-review: I have reviewed the code review findings, and addressed the relevant ones.

Grey Divider

Tip of the day
💡 Did you know, you can add REVIEW.md to your repo root and Qodo follows it on every PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 451eddd

@IsmaelMartinez
IsmaelMartinez merged commit 85c2926 into The-PR-Agent:main Sep 29, 2026
12 checks passed
depohmel added a commit to depohmel/pr-agent that referenced this pull request Oct 16, 2026
* perf(diff): skip fuzzy matching for exact patch lines (The-PR-Agent#3725)

* fix(improve): match applied entries when capping history (The-PR-Agent#3735)

* fix(gerrit): reject unsupported reflect webhook action (The-PR-Agent#3748)

* fix(gitlab): stop search_line from raising UnboundLocalError (The-PR-Agent#3756)

* fix(github_action): fail the Action when a tool records a swallowed failure (The-PR-Agent#3712)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(azure): do not resolve a comment thread after a swallowed tool failure (The-PR-Agent#3722)

* refactor(git-providers): share code suggestion publishing (The-PR-Agent#3726)

* fix(github): fetch all direct sub-issues (The-PR-Agent#3746)

* fix(github): invalidate stale non-empty diff caches (The-PR-Agent#3724)

* docs(github): clarify fork PR checkout and token behavior (The-PR-Agent#3691)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix: fail closed on three silent fallbacks (The-PR-Agent#3742)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(gitea): delete every temp comment on cleanup (The-PR-Agent#3765)

* perf(gitlab): skip impossible file content reads (The-PR-Agent#3761)

* chore: clean up LiteLLM compatibility notes for future upgrades (The-PR-Agent#3764)

* perf(tokens): avoid redundant initial prompt counting (The-PR-Agent#3762)

* perf(diff): stop stalled large-PR packing (The-PR-Agent#3763)

* fix: detect language for multi-part and uppercase suffixes (The-PR-Agent#3767)

* fix(tickets): preserve branch issue order (The-PR-Agent#3766)

* fix(codecommit): prepare comment bodies in publish_code_suggestions (The-PR-Agent#3736)

Co-authored-by: Abreham Melese <[email protected]>

* fix(bitbucket): apply ignore filters to push commands (The-PR-Agent#3768)

Co-authored-by: qyy <[email protected]>

* fix(servers): require a leading slash for comment commands (The-PR-Agent#3769)

Co-authored-by: qyy <[email protected]>

* docs: add kapa.ai "Ask AI" widget (The-PR-Agent#3777)

* fix(mosaico): honor streaming requirements in health probes (The-PR-Agent#3781)

* fix(azure): apply ignore patterns to slash-prefixed paths (The-PR-Agent#3770)

* feat(gitlab): distinguish applied and bot-resolved suggestion threads in the improve context (The-PR-Agent#3774)

* fix(changelog): preserve concurrent edits during changelog updates (The-PR-Agent#3751)

Co-authored-by: Ismael Martinez <[email protected]>

* fix: preserve findings when inline positions cannot be resolved (The-PR-Agent#3753)

* fix: stop three silent fallbacks that corrupt review and label output (The-PR-Agent#3744)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(bitbucket): reject incomplete aggregate pull request diffs (The-PR-Agent#3743)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Ismael Martinez <[email protected]>

* fix(gitlab): classify concurrent changelog creation (The-PR-Agent#3786)

* fix(changelog): make fence stripping idempotent (The-PR-Agent#3783)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(providers): encode filenames in line links (The-PR-Agent#3782)

Co-authored-by: Alex Tumanov <[email protected]>
Co-authored-by: IsmaelMartinez <[email protected]>

* fix(tokens): drop dangling bytes when fitting truncated prompt text (The-PR-Agent#3771)

* chore: fix ruff rule E402 B027 (The-PR-Agent#3772)

Co-authored-by: even-even <>
Co-authored-by: IsmaelMartinez <[email protected]>

* fix(labels): build the label enum when no custom labels are set (The-PR-Agent#3773)

* fix(github): fetch newest polling comments (The-PR-Agent#3747)

Co-authored-by: Alex Tumanov <[email protected]>
Co-authored-by: IsmaelMartinez <[email protected]>

* fix(action): share GitHub helpers so the Action keeps the The-PR-Agent#3670 /ask fix (The-PR-Agent#3785)

* fix(typo): commitable to committable (The-PR-Agent#3749)

Co-authored-by: even-even <>

* fix: stop caching repo context on a mutable ref (The-PR-Agent#3776)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(review): link a TODO without a usable line number to its file (The-PR-Agent#3788)

Co-authored-by: Arta Shoja <[email protected]>

* chore(docs): remove unused direct clsx dependency (The-PR-Agent#3789)

* chore(docs): apply security patches with `npm audit fix` (The-PR-Agent#3790)

* chore: remove redundant CLAUDE.md pointer file (The-PR-Agent#3760)

* fix(servers): route inline comments to /ask_line only on a leading /ask (The-PR-Agent#3797)

* fix(bitbucket): encode filenames in line links (The-PR-Agent#3796)

* fix(tools): re-raise any provider incomplete-files error, not just Bitbucket's (The-PR-Agent#3800)

* fix(models): clamp OpenRouter Gemini reasoning disablement (The-PR-Agent#3787)

Co-authored-by: Alex Tumanov <[email protected]>
Co-authored-by: IsmaelMartinez <[email protected]>

* fix(tickets): cap fetched sub-issues per ticket and order parent before children (The-PR-Agent#3807)

* chore(deps): bump PyJWT from 2.13.0 to 2.15.1 for security fixes (The-PR-Agent#3805)

* chore(deps): address urllib3 and virtualenv security advisories (The-PR-Agent#3804)

* chore: remove unused date and inline-comment helpers (The-PR-Agent#3803)

* fix(git_providers): log publish and settings errors at quiet verbosity (The-PR-Agent#3802)

* chore(release): bump version to 0.47.0 [skip ci]

* feat(review): give /review the findings of earlier reviews (The-PR-Agent#3750)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix: include filtered changed filenames in review context (The-PR-Agent#3806)

Co-authored-by: truongnq <[email protected]>

* fix(file_filter): report skipped and incomplete ignore filtering (The-PR-Agent#3801)

* docs(config): align self-review knobs with the visual-marker-only note (The-PR-Agent#3809)

* fix(deps): declare urllib3 as a direct dependency (The-PR-Agent#3811)

* chore(deps): bump oauthlib to 4.0.0 in uv.lock (The-PR-Agent#3812)

* refactor: remove redundant work from output processing (The-PR-Agent#3813)

* fix(gitea): use the shared slash-command comment gate (The-PR-Agent#3784)

* refactor(prompt): standardize on sandboxed Jinja (The-PR-Agent#3819)

* fix(bitbucket): summarize sensitive webhook payload logs (The-PR-Agent#3818)

* feat: report chunk progress in the progress comment (The-PR-Agent#3814)

* fix(gitea): anchor code suggestions to the new file (The-PR-Agent#3821)

* fix(local): return the branch name string from get_pr_branch (The-PR-Agent#3841)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(gerrit): validate refspec before fetching (The-PR-Agent#3838)

* docs: self-host README images (The-PR-Agent#3839)

* fix(litellm): scope bedrock model_id per model, fix fallback region lookup (The-PR-Agent#3846)

* fix(github): reject file lists collected across PR revisions (The-PR-Agent#3848)

Co-authored-by: Alex Tumanov <[email protected]>

* fix(changelog): preserve confirmed writes after feedback failure (The-PR-Agent#3849)

Co-authored-by: Alex Tumanov <[email protected]>

* fix(gitlab): report suggestion publication failures (The-PR-Agent#3129)

Signed-off-by: teachershuang <[email protected]>

* fix(litellm): skip same-model retry on length truncation (The-PR-Agent#3827)

* fix(webhooks): cap request bodies before parsing (The-PR-Agent#3820)

* fix(improve): make the reflection-failure score configurable (The-PR-Agent#3833)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* feat(models): add OpenAI GPT-6 Sol and Luna support (The-PR-Agent#3628)

* perf(gitlab): stop fetching the project and merge request to react (The-PR-Agent#3851)

* perf(gitlab): read the default branch once instead of per read (The-PR-Agent#3853)

* chore: unskip B017 ruff rule (The-PR-Agent#3856)

Co-authored-by: even-even <>

* perf(yaml): use LibYAML for initial response parsing (The-PR-Agent#3759)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(servers): match the whole /ask command token (The-PR-Agent#3822)

* feat(otel): export per-command token usage (The-PR-Agent#3835)

* fix(security): keep provider endpoints host controlled (The-PR-Agent#3840)

* fix(gitlab): refill ticket context after failed issue lookups (The-PR-Agent#3859)

Co-authored-by: Alex Tumanov <[email protected]>

* fix(improve): report unconfirmed persistent suggestion updates (The-PR-Agent#3858)

Co-authored-by: Alex Tumanov <[email protected]>

* fix(litellm): treat Bedrock ARN overrides as Claude for caching and cost (The-PR-Agent#3844)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix: close LiteLLM streams without interrupting cancellation (The-PR-Agent#3723)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(ask): require https and bound redirects in image check (The-PR-Agent#3847)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* refactor(outputs): separate sink strategies from dispatch (The-PR-Agent#3854)

* feat(gitlab): react to how a comment command ended (The-PR-Agent#3852)

* feat(local): support repo context files in the local git provider (The-PR-Agent#3863)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(gitea): reject incomplete changed-file inventories (The-PR-Agent#3857)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix(ask): sanitize echoed question and line-question answers against quick actions (The-PR-Agent#2629) (The-PR-Agent#3860)

* refactor: make provider SDKs optional (The-PR-Agent#3679)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(improve): name deleted files instead of sending their lines (The-PR-Agent#3866)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(local): write description.md as UTF-8 (The-PR-Agent#3874)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(plain-diff): keep output when stdout cannot encode it (The-PR-Agent#3875)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* feat(push-outputs): add Telegram channel (The-PR-Agent#3868)

* fix(git-providers): prevent fork changelog writes (The-PR-Agent#3905)

Co-authored-by: nightcityblade <[email protected]>

* fix(gitlab): authorize submodule diff targets (The-PR-Agent#3897)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(litellm): isolate request settings (The-PR-Agent#3898)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(config): require config.global_settings_repo for namespace settings (The-PR-Agent#3908)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix(commands): Honor disabled publication in line questions and help (The-PR-Agent#3900)

Co-authored-by: IsmaelMartinez <[email protected]>

* feat(describe): report chunk progress in the progress comment (The-PR-Agent#3872)

* fix: restrict generated labels to the enabled vocabulary (The-PR-Agent#3899)

Co-authored-by: Amazing <[email protected]>

* fix: propagate recorded tool failures from CLI (The-PR-Agent#3919)

* ci: scope docs-ci write to the deploy job and default the other workflows to read-only (The-PR-Agent#3896)

Signed-off-by: Yunare Maia <[email protected]>
Co-authored-by: yunaremaia <[email protected]>

* fix(mosaico): limit requests and isolate caller tasks (The-PR-Agent#3926)

* fix(servers): verify Gitea and GitLab webhooks before parsing (The-PR-Agent#3912)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* fix: preserve optional provider install hints in context errors (The-PR-Agent#3929)

Co-authored-by: strawberry-little-bear <[email protected]>

* fix(ignore): allow globstar segments to match zero directories (The-PR-Agent#3855)

* fix(telemetry): configure delta aggregation temporality for prometheus counter exporter (The-PR-Agent#3871) (The-PR-Agent#3873)

* fix(review): mark PR title/description/commits/tickets/code as untrusted in reviewer system prompt (The-PR-Agent#3921)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix(gitea): skip the base-content fetch for added files (The-PR-Agent#3936)

* chore(deps): update LiteLLM to 1.104.0 (The-PR-Agent#3932)

* fix(gitea): parse PR and issue URLs of an instance served under a subpath (The-PR-Agent#3937)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(tickets): Match full issue URLs on the configured GitHub origin (The-PR-Agent#3907)

Co-authored-by: IsmaelMartinez <[email protected]>

* feat(models): add OpenAI GPT-6.1 Sol support (The-PR-Agent#3938)

* fix(gerrit): keep line endings and mode when applying a suggestion (The-PR-Agent#3876)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix: restrict host-only comment arguments (The-PR-Agent#3902)

Co-authored-by: nightcityblade <[email protected]>

* perf(git_providers): memoize get_languages() across providers (The-PR-Agent#3934)

* fix(git_providers): bound HTTP requests that have no timeout (The-PR-Agent#3850)

* fix(docker): Run services as non-root and pin build images (The-PR-Agent#3910)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix(gitlab): Verify stored webhook tokens before loading credentials (The-PR-Agent#3913)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix(tickets): restrict GitHub issue reads to approved repositories (The-PR-Agent#3922)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix: load repository settings from target refs (The-PR-Agent#3906)

Co-authored-by: nightcityblade <[email protected]>

* fix(webhooks): Require Azure and Bitbucket Server credentials (The-PR-Agent#3911)

Co-authored-by: IsmaelMartinez <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* chore(ci): bump the actions-minor-and-patch group with 2 updates (The-PR-Agent#3940)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(config): stop loading [tool.pr-agent] from the checkout's pyproject.toml (The-PR-Agent#3941)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* docs: narrow installation permissions (The-PR-Agent#3901)

Co-authored-by: nightcityblade <[email protected]>

* fix(security): protect provider connection settings (The-PR-Agent#3904)

Co-authored-by: nightcityblade <[email protected]>

* feat(github): stream chunk progress into in-progress check runs (The-PR-Agent#3933)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* chore(deps): bump proxy-addr from 2.0.7 to 2.0.8 in /docs (The-PR-Agent#3946)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump source-map-js in /docs and make the uv pin a floor for Dependabot (The-PR-Agent#3949)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>

* chore(deps): bump fsspec from 2025.12.0 to 2026.6.0 (The-PR-Agent#3953)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump langgraph-sdk from 0.4.2 to 0.4.4 (The-PR-Agent#3952)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps): bump multidict from 6.7.1 to 6.9.1 (The-PR-Agent#3954)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat(review): treat human-resolved inline key-issue threads as dismissed (The-PR-Agent#3925)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(bitbucket): align Connect lifecycle and JWT handling (The-PR-Agent#3877) (The-PR-Agent#3923)

Co-authored-by: Ankita-Advitot <[email protected]>

* fix(security): reject dunder section/key names in repo settings files (The-PR-Agent#3947)

Co-authored-by: IsmaelMartinez <[email protected]>

* chore(deps): bump shell-quote from 1.10.0 to 1.12.0 in /docs (The-PR-Agent#3957)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(diff): use fixed context for headerless hunks (The-PR-Agent#3955)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix: keep provider auth and TLS settings host-only (The-PR-Agent#3950)

Co-authored-by: insisong <[email protected]>

* fix(local): respect the configured description token budget (The-PR-Agent#3956)

* chore: unskip B904 ruff rule (The-PR-Agent#3951)

Co-authored-by: even-even <>
Co-authored-by: IsmaelMartinez <[email protected]>

* fix(context): avoid duplicate local repository context entries (The-PR-Agent#3968)

* fix(diff): extend zero-length hunks from the right line in extend_patch (The-PR-Agent#3966)

Co-authored-by: Claude Sonnet 5.5 <[email protected]>

* fix(polling): preserve pulls segment in comment URLs (The-PR-Agent#3962)

* fix(bitbucket): handle push events without a pull request (The-PR-Agent#3961)

* fix(gitea): read diff file names with the shared parser (The-PR-Agent#3964)

* fix: enforce PR ignore policy at the agent boundary (The-PR-Agent#3943)

* fix(review): handle non-list can_be_split values (The-PR-Agent#3969)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix(review): log all schema validation errors and run require checks (The-PR-Agent#3973) (The-PR-Agent#3974)

Co-authored-by: Ankita-Advitot <[email protected]>

* chore: unskip B023 ruff rule (The-PR-Agent#3977)

Co-authored-by: even-even <>

* fix(gitea): reuse the PR commit snapshot for messages (The-PR-Agent#3971)

* fix(ask): propagate final reply delivery errors (The-PR-Agent#3970)

* fix(context): prioritize directly linked ticket context (The-PR-Agent#3967)

* fix(bitbucket): verify the webhook JWT before parsing the request body (The-PR-Agent#3958)

* feat(tickets): include native issue references from PR titles (The-PR-Agent#3980)

* perf(diff): reuse patch token counts across packing rounds (The-PR-Agent#3986)

* perf(context): load instruction files lazily by line budget (The-PR-Agent#3985)

* fix(litellm): align OpenRouter reasoning clamps and token reserves (The-PR-Agent#3981)

* fix(github): resolve threads for replies after the first comment page (The-PR-Agent#3979)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix: preserve extra instructions in suggestion reflection (The-PR-Agent#3987)

* fix: mark CI artifact input as untrusted (The-PR-Agent#3978)

Signed-off-by: 张峻祎 <[email protected]>
Signed-off-by: NekoPaw <[email protected]>
Co-authored-by: 张峻祎 <[email protected]>
Co-authored-by: Ismael Martinez Ramos <[email protected]>

* chore: unskip E722 ruff rule and actualize ruff config (The-PR-Agent#3993)

Co-authored-by: even-even <>
Co-authored-by: IsmaelMartinez <[email protected]>

* fix(review): escape HTML, convert newlines to <br>, and render nested YAML in unknown review keys (The-PR-Agent#3975) (The-PR-Agent#3976)

Co-authored-by: Ankita-Advitot <[email protected]>

* Allow extra review fields (The-PR-Agent#3998)

* fix(jira): keep jira_site and project_keys host-only (The-PR-Agent#4001)

* fix: create missing AWS secrets (The-PR-Agent#3930)

* fix(review): render url-less ticket compliance without dead link (The-PR-Agent#4000)

* perf(gerrit): walk the commit diff once per provider instance (The-PR-Agent#3935)

* fix(review): allow disabling failure comments (The-PR-Agent#4011)

Co-authored-by: Alex Tumanov <[email protected]>

* fix(bitbucket-server): build line links on the canonical PR URL (The-PR-Agent#4015)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* perf(diff): use set for included-file metadata checks (The-PR-Agent#4017)

* fix(gerrit): reset local edits after failed patch uploads (The-PR-Agent#4021)

* fix(review): keep url-less ticket compliance entries distinct (The-PR-Agent#3999)

* perf(routing): count diff hunks only when needed (The-PR-Agent#4008)

* fix(litellm): warn on invalid config.max_output_tokens and enforce a 4096 floor (The-PR-Agent#4013)

Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix: report clipped patches as partial input coverage (The-PR-Agent#4019)

Co-authored-by: IsmaelMartinez <[email protected]>

* fix(gitea): keep failed guidance reads out of caches (The-PR-Agent#4016)

Co-authored-by: IsmaelMartinez <[email protected]>

* chore: rename merge_recommendation values to describe findings (The-PR-Agent#4010)

* feat(artifacts): choose which end of the file truncation keeps (The-PR-Agent#3990)

* feat(review): add opt-in failure modes (The-PR-Agent#4012)

Co-authored-by: Alex Tumanov <[email protected]>
Co-authored-by: IsmaelMartinez <[email protected]>
Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>

* feat(review): honor explicit Azure key-issue dismissals (The-PR-Agent#4025)

* fix(bitbucket): report request failures before falling back (The-PR-Agent#4022)

* fix(security): reject non-public destination address ranges (The-PR-Agent#4024)

* fix(improve): fence a suggestion with more backticks when its code has a fence (The-PR-Agent#3791)

Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Ismael Martinez Ramos <[email protected]>

* fix: redact CI artifact credentials before prompt injection (The-PR-Agent#3989)

* fix: find suggestion fence closers without a lazy scan (The-PR-Agent#4028)

* fix: account for CI in merge recommendation (The-PR-Agent#4031)

* fix: pass CI artifact context into suggestion self-reflection (The-PR-Agent#4034)

* chore: remove unused code suggestion parser (The-PR-Agent#4035)

* feat: export PR reviews as JSON (The-PR-Agent#4033)

* fix(gitlab): load host settings before note provider lookup (The-PR-Agent#4023)

* fix: improve skills budget warnings (The-PR-Agent#3865)

* feat: cache the shared skills prefix across review tools (The-PR-Agent#4014)

* fix(gerrit): skip symlink and git-metadata suggestion paths (The-PR-Agent#3915)

Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>

* fix: skip empty insights section in review output (The-PR-Agent#4038)

* fix(openrouter): attribute model requests to PR-Agent (The-PR-Agent#4039)

* fix(diff): keep unreadable file notices in chunked diffs (The-PR-Agent#4041)

* perf(github): resolve merge base only when needed (The-PR-Agent#4044)

* fix(github): avoid retrying permanent diff collection failures (The-PR-Agent#4045)

* fix: tolerate missing effort field in review labels (The-PR-Agent#4047)

* fix: merge extra config keys case-insensitively (The-PR-Agent#4037)

* feat(review): honor human-resolved GitHub key issues (The-PR-Agent#4032)

* feat(ci): enable PR Agent on PR Agent (The-PR-Agent#4002)

* fix(ci): read the comment event from GITHUB_EVENT_PATH in PR-Agent authorization (The-PR-Agent#4050)

* feat(ci): post PR-Agent output as the PR Agent GitHub App when configured (The-PR-Agent#4051)

* chore(models): prune retired and obsolete static model entries (The-PR-Agent#4026)

* fix: keep review finding location when end line is empty (The-PR-Agent#4036)

* chore: up html2text to 2025.4.15 (The-PR-Agent#4055)

Co-authored-by: even-even <>

* perf(diff): bound tokenization when trimming filename metadata (The-PR-Agent#4056)

* fix(models): correct Claude Sonnet 4.6 and Opus 4.6 context windows (The-PR-Agent#4058)

* fix(improve): avoid model fallback for empty analyzable diffs (The-PR-Agent#4057)

* fix(azure): surface diff collection failures (The-PR-Agent#4060)

* fix: restrict LiteLLM callbacks to deployment settings (The-PR-Agent#4054)

* fix: cap github suggestion body length (The-PR-Agent#4053)

* fix: keep quoted command setting values as strings (The-PR-Agent#4048)

* fix(litellm): observe detached stream cleanup failures (The-PR-Agent#4042)

* chore: remove unused JSON repair helpers (The-PR-Agent#4061)

* fix: close file handle in bitbucket manifest handler (The-PR-Agent#4063)

* fix: preserve yaml-prefixed keys in load_yaml (The-PR-Agent#4069)

* fix(docs): preserve Azure DevOps dismissal note during regeneration (The-PR-Agent#4066)

* docs(litellm): clarify shared skills and per-tool cache breakpoints (The-PR-Agent#4065)

* fix(settings): redact shared errors and retry transient fetches (The-PR-Agent#4071)

* perf(yaml): avoid reparsing unchanged failed input (The-PR-Agent#4073)

* fix(tickets): deduplicate GitHub issue references by identity (The-PR-Agent#4072)

* docs: add Heabsy to changing_a_model (The-PR-Agent#3755)

* docs: add DemonRoute to changing_a_model (The-PR-Agent#4074)

* docs: use a concrete DemonRoute model in example (The-PR-Agent#4075)

* Document RelaxAI OpenAI-compatible setup (The-PR-Agent#4076)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: naorpeled <[email protected]>

* feat: add Nemotron reasoning model support with configurable trigger (#2)

- Add nemotron_reasoning_models and nemotron_reasoning_trigger config options
- Add NEMOTRON_REASONING_MODELS constant in pr_agent/algo/__init__.py
- Add _apply_nemotron_trigger() helper in LiteLLMAIHandler
- Apply trigger on normal path (before user_message_only/custom_reasoning_model)
- Add real unit tests in tests/unittest/test_nemotron_reasoning.py

* feat: add review gate to enforce APPROVE/REQUEST_CHANGES verdict (#1)

* fix(review-gate): empty/stringified key_issues -> APPROVE; readable rendering; type-safe security_concerns (#3)

* security: build reviewer image from audited fork source (#4)

* docs: Fix Dockerfile path in gitea installation instructions (The-PR-Agent#2524)

Previously an inexistant absolute path is used, leads to a file not found error.

* chore(deps): Upgrade LiteLLM to the latest stable version v1.92.0 (The-PR-Agent#2521)

The reviewed releases contain no project-wide breaking changes and add
model mappings and broader provider coverage, plus improvements to
streaming, routing, observability, and security hardening. The upstream
LiteLLM image OpenSSL patch does not apply to this project's base image.

Stable release lines reviewed:
v1.84.x (v1.84.0-v1.84.10), v1.85.x (v1.85.0-v1.85.7),
v1.86.x (v1.86.0-v1.86.7), v1.87.x (v1.87.0-v1.87.5),
v1.88.x (v1.88.0-v1.88.5), v1.89.x (v1.89.0-v1.89.6),
v1.90.x (v1.90.0-v1.90.4), v1.91.x (v1.91.0-v1.91.3),
and v1.92.0.

Stable release references:
- https://pypi.org/project/litellm/1.84.0/
- https://pypi.org/project/litellm/1.84.1/
- https://pypi.org/project/litellm/1.84.2/
- https://pypi.org/project/litellm/1.84.3/
- https://pypi.org/project/litellm/1.84.4/
- https://pypi.org/project/litellm/1.84.5/
- https://pypi.org/project/litellm/1.84.6/
- https://pypi.org/project/litellm/1.84.7/
- https://pypi.org/project/litellm/1.84.8/
- https://pypi.org/project/litellm/1.84.9/
- https://pypi.org/project/litellm/1.84.10/
- https://pypi.org/project/litellm/1.85.0/
- https://pypi.org/project/litellm/1.85.1/
- https://pypi.org/project/litellm/1.85.2/
- https://pypi.org/project/litellm/1.85.3/
- https://pypi.org/project/litellm/1.85.4/
- https://pypi.org/project/litellm/1.85.5/
- https://pypi.org/project/litellm/1.85.6/
- https://pypi.org/project/litellm/1.85.7/
- https://pypi.org/project/litellm/1.86.0/
- https://pypi.org/project/litellm/1.86.1/
- https://pypi.org/project/litellm/1.86.2/
- https://pypi.org/project/litellm/1.86.3/
- https://pypi.org/project/litellm/1.86.4/
- https://pypi.org/project/litellm/1.86.5/
- https://pypi.org/project/litellm/1.86.6/
- https://pypi.org/project/litellm/1.86.7/
- https://pypi.org/project/litellm/1.87.0/
- https://pypi.org/project/litellm/1.87.1/
- https://pypi.org/project/litellm/1.87.2/
- https://pypi.org/project/litellm/1.87.3/
- https://pypi.org/project/litellm/1.87.4/
- https://pypi.org/project/litellm/1.87.5/
- https://pypi.org/project/litellm/1.88.0/
- https://pypi.org/project/litellm/1.88.1/
- https://pypi.org/project/litellm/1.88.2/
- https://pypi.org/project/litellm/1.88.3/
- https://pypi.org/project/litellm/1.88.4/
- https://pypi.org/project/litellm/1.88.5/
- https://pypi.org/project/litellm/1.89.0/
- https://pypi.org/project/litellm/1.89.1/
- https://pypi.org/project/litellm/1.89.2/
- https://pypi.org/project/litellm/1.89.3/
- https://pypi.org/project/litellm/1.89.4/
- https://pypi.org/project/litellm/1.89.5/
- https://pypi.org/project/litellm/1.89.6/
- https://pypi.org/project/litellm/1.90.0/
- https://pypi.org/project/litellm/1.90.1/
- https://pypi.org/project/litellm/1.90.2/
- https://pypi.org/project/litellm/1.90.3/
- https://pypi.org/project/litellm/1.90.4/
- https://pypi.org/project/litellm/1.91.0/
- https://pypi.org/project/litellm/1.91.1/
- https://pypi.org/project/litellm/1.91.2/
- https://pypi.org/project/litellm/1.91.3/
- https://pypi.org/project/litellm/1.92.0/

* AI: fix(gitea): allow description updates without titles (The-PR-Agent#2526)

* fix(gitea): allow description updates without titles

Make the Gitea pull request edit wrapper accept an omitted title and only include the title field in the request when one is supplied. This preserves manually edited titles when AI title generation is disabled.

Add coverage for description updates both with and without a title.

Fixes The-PR-Agent#2525.

Assisted-by: Codex:GPT-5.6
Tested-by: 林博仁(Buo-ren Lin) <[email protected]>
Signed-off-by: 林博仁(Buo-ren Lin) <[email protected]>

* lint(gitea): Conform to the project's quoting style requirements.

Signed-off-by: 林博仁(Buo-ren Lin) <[email protected]>

---------

Signed-off-by: 林博仁(Buo-ren Lin) <[email protected]>

* Honor reasoning_effort for Gemini 2.5 (and provider-prefixed reasoning models) (The-PR-Agent#2520)

SUPPORT_REASONING_EFFORT_MODELS contained only bare o3/o4 ids, and the gate in
LiteLLMAIHandler.chat_completion used an exact-string `model in
self.support_reasoning_models` test. So a configured `reasoning_effort` was
silently dropped for two cases:

  1. Gemini 2.5 (pro/flash), which support a thinking budget LiteLLM maps from
     reasoning_effort — they were never in the list.
  2. Any reasoning model referenced with a provider prefix (e.g.
     "openrouter/o3", "openrouter/google/gemini-2.5-pro"), because the prefixed
     string never equals the bare id.

For Gemini this matters beyond a missing knob: with reasoning uncapped, a runaway
thinking trace can consume the whole output budget and return an empty completion.

- Add gemini-2.5-pro / gemini-2.5-flash to SUPPORT_REASONING_EFFORT_MODELS.
- Match the bare id OR any "<provider>/<id>" suffix form, so prefixed configs are
  covered without over-matching (e.g. "my-gemini-2.5-pro" is not matched).
- Tests covering prefixed forms, non-listed Gemini (1.5), and the no-overmatch
  boundary.

Co-authored-by: Claude (agent-generated) <[email protected]>

* feat(openrouter): add provider routing and reasoning controls (The-PR-Agent#2518)

* feat(openrouter): add provider routing and reasoning controls

Add an [openrouter] configuration section that lets operators restrict which
upstream providers OpenRouter uses, control reasoning, and cap completion
length for models addressed as "openrouter/...".

The LiteLLM handler now injects, only for openrouter/* models:
- extra_body.provider.only (hard allowlist) or provider.order + allow_fallbacks
- extra_body.reasoning (effort, "none" to disable, or a max_tokens budget)
- a hard max_tokens cap on the completion

Every key defaults to unset, so the block is a no-op unless configured and never
affects other providers. Includes documentation and unit tests covering provider
routing, reasoning control, the output cap, and the no-op and non-openrouter cases.

Refs: sparkfabrik-innovation-team/board#4593
Assisted-by: claude-code/claude-opus-4-8

* fix(openrouter): cap max_tokens, fix test isolation and mock

Address review feedback:
- openrouter.max_tokens now caps kwargs["max_tokens"] (min with any value set
  earlier) instead of overwriting it, matching the documented hard-cap semantics.
- Follow the repository's MagicMock response pattern (__getitem__.side_effect and
  dict.return_value) in the unit test.
- Snapshot and restore the global litellm/openai state that LiteLLMAIHandler.__init__
  mutates, so the tests no longer leak state across the suite.

Refs: sparkfabrik-innovation-team/board#4593
Assisted-by: claude-code/claude-opus-4-8

* fix(openrouter): keep openrouter prefix when azure mode is enabled

Azure mode rewrote every model to "azure/<model>", so an "openrouter/..." model
became "azure/openrouter/...": the request was misrouted away from the OpenRouter
api_key/api_base configured in __init__, and the OpenRouter controls block (guarded
by the "openrouter/" prefix) was silently skipped. Exclude openrouter models from
the azure rewrite, following the existing databricks guard, and add a regression
test for azure mode with an openrouter model.

Refs: sparkfabrik-innovation-team/board#4593
Assisted-by: claude-code/claude-fable-5

* fix(openrouter): isolate handler env vars in unit tests

LiteLLMAIHandler.__init__ reads AWS_USE_IMDS and, when set, mutates the AWS_*
credential variables in os.environ; it also reads OPENAI_API_KEY. The autouse
fixture now snapshots and restores these variables and drops AWS_USE_IMDS for
the duration of each test, so the AWS credential path never runs and the tests
stay deterministic regardless of the host environment.

Refs: sparkfabrik-innovation-team/board#4593
Assisted-by: claude-code/claude-fable-5

* fix(gitlab): allow overriding is_bot_user indicators  (The-PR-Agent#2528)

* feat(gitlab): allow overriding is_bot_user indicators via gitlab.bot_user_indicators

Introduces a new optional GitLab setting that lets operators replace the
hardcoded bot-name substrings used to skip GitLab webhook events. When
unset, behavior is unchanged (defaults to
['codium', 'bot_', 'bot-', '_bot', '-bot']). Operators can now include
sender names like 'renovate' or 'dependabot' without patching the code.

Adds unit coverage for both the default list and the override path.

* fix(gitlab): normalize bot_user_indicators and centralize default in configuration.toml

Addresses Qodo review feedback on The-PR-Agent#2528:

- Move the default list from a Python literal to a real Dynaconf default in
  configuration.toml so there's one source of truth (fix: hardcoded default).
- Normalize the value read from settings: coerce a bare string to a single-item
  list, drop non-string entries, and log-and-empty on non-iterable values so a
  misconfigured .pr_agent.toml can't crash bot detection (fix: input validation).
- Lowercase indicators before matching so operator-supplied entries like
  "Renovate" behave case-insensitively as the doc comment claims (fix: case bug).
- Tighten the configuration.toml comment to state plainly that overrides REPLACE
  the default list (fix: ambiguous "extend or replace" wording).

Adds three new tests: case-insensitive matching, string-not-list normalization
(with an explicit guard against per-character iteration), and non-string entry
filtering. Existing default-indicator tests simplified now that the TOML entry
is authoritative.

* refactor(config): move bot_user_indicators from [gitlab] to [config] for reuse

Follow-up to review feedback on The-PR-Agent#2528 from @naorpeled: generalize the setting
so other providers can adopt it later.

The setting now lives under [config] alongside ignore_pr_authors and the
other cross-provider filters. Only the GitLab webhook consumes it today;
follow-up work can wire it into bitbucket_server_webhook, gitea_app, or any
other provider whose payload lacks a native bot flag. GitHub and Bitbucket
Cloud already use provider-native type flags (sender.type == 'Bot',
actor.type) and should keep doing so — those signals are higher fidelity
than a substring list.

- pr_agent/settings/configuration.toml: move bot_user_indicators from
  [gitlab] to [config]; reword the comment to be provider-neutral.
- pr_agent/servers/gitlab_webhook.py: read from
  config.bot_user_indicators instead of gitlab.bot_user_indicators; update
  the inline comment to note the setting is intentionally cross-provider.
- tests/unittest/test_webhook_logic_core.py: rename all four
  GITLAB.BOT_USER_INDICATORS references to CONFIG.BOT_USER_INDICATORS.
  All 17 tests continue to pass; no behavioral change.

* Add Dockerfile for building pr-agent from source to address security concerns

This Dockerfile builds the PR-Agent application from source instead of relying on the unverified base image pragent/pr-agent:0.39.0-github_app. This addresses security concerns by ensuring that the reviewer image is built from our audited fork source rather than potentially unverified Docker Hub base images.

* Security: Implement reviewer image from fork source

This PR implements the security enhancement to replace the unverified base image with a source-built Docker image. The new Dockerfile builds the pr-agent application directly from our audited fork source instead of relying on potentially unverified Docker Hub base images.

---------

Signed-off-by: 林博仁(Buo-ren Lin) <[email protected]>
Co-authored-by: 林博仁 Buo-ren Lin <[email protected]>
Co-authored-by: Peter Dave Hello <[email protected]>
Co-authored-by: Mattias Petter Johansson <[email protected]>
Co-authored-by: Claude (agent-generated) <[email protected]>
Co-authored-by: Fabrizio Cafolla <[email protected]>
Co-authored-by: Allen Shearin <[email protected]>

* fix(image): harden reviewer container - pin deps and drop root privileges (#5)

* build: add Dockerfile to build reviewer from fork source with digest-pinned base

Adds a root Dockerfile that builds the github_app reviewer image directly
from this fork's source (pip install . + pr_agent source), replacing the
unverified pragent/pr-agent:0.39.0-github_app Docker Hub base. The image is
a drop-in: same WORKDIR (/app), same CMD (gunicorn serving
pr_agent.servers.github_app:app), same exposed port 3000/tcp, no ENTRYPOINT,
PYTHONPATH=/app. Base image python:3.12.13-slim is pinned by sha256 digest.
No secrets are baked in (.dockerignore excludes .secrets.toml/.env; secrets
are bind-mounted at runtime from ~/.pr-agent/.secrets.toml).

* docs: document reviewer image swap and rollback procedure

Adds docs/docs/installation/reviewer_image_swap.md with exact build,
verify, push, redeploy and rollback commands for swapping the running
pr-agent-reviewer container to the fork-built image, plus the runtime
contract (name, restart policy, port 127.0.0.1:3033->3000, secrets
bind-mounts) and a note that pr-agent-poller is unaffected. Registers the
page in the MkDocs nav under a new Operations section.

* fix: copy all build inputs to Dockerfile so pip install . installs the real package

The original Dockerfile ran `pip install .` with only pyproject.toml/requirements.txt/docs
in the context. pyproject.toml declares readme=README.md and license={file=LICENSE},
and setuptools needs the pr_agent package to produce a real wheel. Without README.md,
LICENSE, MANIFEST.in and pr_agent present the build yields a metadata-only wheel
(no modules installed). Copy all build inputs before installing, so pr_agent is
genuinely installed rather than relying on PYTHONPATH at runtime.

* fix(image): harden reviewer container - pin deps and drop root privileges

Two security findings from upstream code review:

Finding 1 - floating build dependencies:
- APT: pin git and curl to exact Debian 13 (trixie) versions in the same
  apt-get install line; apt lists cleaned in the same layer.
- Python: generate a fully-pinned, hash-locked requirements.lock via
  pip-compile --generate-hashes (Python 3.12, linux/amd64) and install with
  --require-hashes.  The package itself is installed with --no-deps so pip
  does not re-resolve the >= ranges in requirements.txt.  This makes two
  builds of the same commit produce identical dependency sets.
- What cannot be pinned: the APT *mirror* is not frozen to a Debian snapshot
  — only the resolved package versions are pinned.  A future improvement
  could pin the snapshot URL or use apt-pinning to a specific release.

Finding 2 - runs as root:
- Create an unprivileged system user (appuser, uid 999) with a writable
  home directory (needed by azure-devops and litellm at import time).
- chown /app and /home/appuser to appuser; make the settings dir
  world-readable (it contains no secrets in the image).
- Switch to USER appuser before the CMD.
- Runtime note: the bind-mounted .secrets.toml must be readable by the
  appuser UID (e.g. chmod 644 on the host file).

Verified:
- docker build -t pr-agent-fork-harden -f Dockerfile . → BUILD_EXIT=0
- docker run --rm --entrypoint sh pr-agent-fork-harden -c "id -u" → 999
- docker run --rm --entrypoint sh ... -c 'python -c "from pr_agent.servers.github_app import app"' → OK
- docker run ... gunicorn binds to port, 4 workers boot, app startup complete

* fix(config): read nemotron reasoning settings with safe defaults

* test: populate nemotron config in mock config.get()

* fix: clean up placeholder on review failure, dismiss stale bot CHANGES_REQUESTED, raise gunicorn timeout (#7)

Resolved cherry-pick conflicts with fork/main (which has review-gate,
request_changes, and parsed_review_data additions).

BUG 1: try/finally ensures remove_initial_comment() runs on every exit
path - normal return, early return, and exception. Without this, a
failed LLM call leaves 'Preparing review...' on the PR forever.

BUG 2: dismiss_stale_changes_requested_reviews() added to GithubProvider.
When a fresh review of the current head finds no key issues and no
security concerns, the bot's own prior CHANGES_REQUESTED reviews are
dismissed via GitHub's PUT /reviews/{id}/dismissals endpoint. Only
bot-authored reviews are ever dismissed - never a human's.

Gunicorn timeout raised from 240s to 600s (configurable via
GUNICORN_TIMEOUT env var) to match ai_timeout, preventing the master
from SIGABRTing the worker mid-review.

* fix(providers): add missing should_publish_review_as_thread and create_review (#9)

* docs: update configuration reference for nemotron and review-gate keys

The new-base Docusaurus config-reference test requires the generated page to match configuration.toml; regenerate after the fork's nemotron_reasoning_* and enable_review_gate keys land.

* fix: guard dismiss_stale call for providers lacking the method

The guard uses getattr so hand-rolled provider test doubles (and any third-party provider not inheriting the GitProvider base no-op) do not crash clean reviews. Also drops the NEMOTRON_REASONING_MODELS import in litellm_ai_handler.py, which became unused after the merge kept the base's config .get() pattern.

* style: wrap long comment flagged by ruff E501

* style: fix ruff F401/E501 in fork test files

* test: bump config key count constant for the 3 fork-added keys

The new base's test hardcodes the active key count (287). The fork adds nemotron_reasoning_models, nemotron_reasoning_trigger and enable_review_gate, so the count is 290 on this branch.

* docs: add Docusaurus front matter and sidebar entry to reviewer image page

The fork's reviewer_image_swap page landed via the cherry-picked hardening commit without the YAML front matter or sidebars.js registration that the new base's docs-nav parity tests require.

* style: apply pre-commit trailing-whitespace and end-of-file-fixer fixes

* fix: initialize code suggestion publication status

* fix: validate GitHub API URL hosts precisely

* test: enable jinja2 autoescape in prompt-fragment tests (CodeQL py/jinja2/autoescape-false)

---------

Signed-off-by: teachershuang <[email protected]>
Signed-off-by: Yunare Maia <[email protected]>
Signed-off-by: dependabot[bot] <[email protected]>
Signed-off-by: 张峻祎 <[email protected]>
Signed-off-by: NekoPaw <[email protected]>
Signed-off-by: 林博仁(Buo-ren Lin) <[email protected]>
Co-authored-by: Peter Dave Hello <[email protected]>
Co-authored-by: Utsab Dahal <[email protected]>
Co-authored-by: Alex Tumanov <[email protected]>
Co-authored-by: charlieanna <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
Co-authored-by: Grapette.L <[email protected]>
Co-authored-by: OUAO-FRANK <[email protected]>
Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Ismael Martinez Ramos <[email protected]>
Co-authored-by: Abreham Melese <[email protected]>
Co-authored-by: Abreham Melese <[email protected]>
Co-authored-by: Yoel-qin <[email protected]>
Co-authored-by: qyy <[email protected]>
Co-authored-by: Naor Peled <[email protected]>
Co-authored-by: JWM <[email protected]>
Co-authored-by: Ismael Martinez <[email protected]>
Co-authored-by: strawberry-little-bear <[email protected]>
Co-authored-by: Alex Tumanov <[email protected]>
Co-authored-by: Vladimir Roshchin <[email protected]>
Co-authored-by: Arta <[email protected]>
Co-authored-by: Arta Shoja <[email protected]>
Co-authored-by: Kudala Bharani Kumar Reddy <[email protected]>
Co-authored-by: Pranav Deshpande <[email protected]>
Co-authored-by: Hrishikesh Yadav <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Hugo Knorr <[email protected]>
Co-authored-by: Tony Nguyen <[email protected]>
Co-authored-by: truongnq <[email protected]>
Co-authored-by: NekoPaw <[email protected]>
Co-authored-by: Felix Schaefer <[email protected]>
Co-authored-by: Peace. A <[email protected]>
Co-authored-by: teachershuang <[email protected]>
Co-authored-by: Erez Lev <[email protected]>
Co-authored-by: NesrineGharbi77 <[email protected]>
Co-authored-by: Anjankumar LN <[email protected]>
Co-authored-by: noooooooookro <[email protected]>
Co-authored-by: nightcityblade <[email protected]>
Co-authored-by: nightcityblade <[email protected]>
Co-authored-by: Brown <[email protected]>
Co-authored-by: Amazing <[email protected]>
Co-authored-by: abhi26 <[email protected]>
Co-authored-by: Yunare Maia <[email protected]>
Co-authored-by: yunaremaia <[email protected]>
Co-authored-by: strawberry-little-bear <[email protected]>
Co-authored-by: Abhinav Maurya <[email protected]>
Co-authored-by: Abhishek Jha <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: AnkitaAdvitot <[email protected]>
Co-authored-by: Ankita-Advitot <[email protected]>
Co-authored-by: Isaac Emmanuel <[email protected]>
Co-authored-by: insisong <[email protected]>
Co-authored-by: Havi007 <[email protected]>
Co-authored-by: Evhye38496 <[email protected]>
Co-authored-by: Eastern <[email protected]>
Co-authored-by: 张峻祎 <[email protected]>
Co-authored-by: Jin <[email protected]>
Co-authored-by: jiuyueQ <[email protected]>
Co-authored-by: Harsh Raj Singhania <[email protected]>
Co-authored-by: karthi040506 <[email protected]>
Co-authored-by: ofir-frd <[email protected]>
Co-authored-by: Allen Shearin <[email protected]>
Co-authored-by: kafomichev <[email protected]>
Co-authored-by: Demon Route <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: naorpeled <[email protected]>
Co-authored-by: 林博仁 Buo-ren Lin <[email protected]>
Co-authored-by: Mattias Petter Johansson <[email protected]>
Co-authored-by: Fabrizio Cafolla <[email protected]>
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.

Azure DevOps comment handler resolves the thread after a swallowed tool failure

2 participants