Skip to content

feat: add Asana ticket detection to ticket compliance check#2430

Open
Oxygen56 wants to merge 17 commits into
The-PR-Agent:mainfrom
Oxygen56:feat/asana-ticket-provider
Open

feat: add Asana ticket detection to ticket compliance check#2430
Oxygen56 wants to merge 17 commits into
The-PR-Agent:mainfrom
Oxygen56:feat/asana-ticket-provider

Conversation

@Oxygen56

@Oxygen56 Oxygen56 commented Jun 6, 2026

Copy link
Copy Markdown

What

Fixes #2002 — Adds Asana task references detection to the PR ticket compliance check.

Problem

The ticket compliance check only detected GitHub issues and JIRA tickets. Teams using Asana had no automated ticket validation.

Solution

Adds find_asana_tickets() that detects:

  • Full Asana URLs: https://app.asana.com/0/{project_id}/{task_id}
  • Shorthand format: ASANA-123456789012

Asana references are included in the compliance check's related tickets alongside GitHub and JIRA references.

@github-actions github-actions Bot added the feature 💡 label Jun 6, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Review Summary by Qodo

Add Asana ticket detection and fix indentation normalization

✨ Enhancement 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• Add Asana ticket detection to PR compliance check
  - Supports full URLs and ASANA-123456789 shorthand format
  - Asana tickets included in related tickets list
• Fix indentation character preservation in /improve suggestions
  - Handle negative delta_spaces with dedent operation
  - Normalize tabs/spaces to match original file's indentation
Diagram
flowchart LR
  A["PR Description & Branch"] -->|find_asana_tickets| B["Asana URLs Detected"]
  B -->|extract_tickets| C["Merged Ticket List"]
  C -->|Asana URLs| D["Placeholder Content"]
  E["Code Suggestions"] -->|dedent_code| F["Normalize Indentation"]
  F -->|Detect char| G["Tab or Space"]
  G -->|Apply| H["Corrected Code"]

Loading

Grey Divider

File Changes

1. pr_agent/tools/pr_code_suggestions.py 🐞 Bug fix +18/-2

Fix indentation character preservation in code suggestions

• Move indentation character detection outside conditional block
• Add handling for negative delta_spaces using textwrap.dedent
• Normalize all suggestion lines to match original file's indentation character
• Convert spaces to tabs when original file uses tab indentation

pr_agent/tools/pr_code_suggestions.py


2. pr_agent/tools/ticket_pr_compliance_check.py ✨ Enhancement +49/-0

Add Asana ticket detection to compliance check

• Add find_asana_tickets() function to detect Asana task references
• Support both full Asana URLs and ASANA-123456789 shorthand format
• Integrate Asana ticket detection into extract_tickets() workflow
• Handle Asana URLs with placeholder content since they cannot be fetched via GitHub API

pr_agent/tools/ticket_pr_compliance_check.py


Grey Divider

Qodo Logo

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

qodo-free-for-open-source-projects Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (4) 📎 Requirement gaps (1) 📜 Skill insights (0)

Context used

Grey Divider


Action required

1. extract_tickets() can return None 📘 Rule violation ☼ Reliability
Description
extract_tickets() only returns when tickets_content is truthy, so callers can receive None
when no tickets are found or when an exception occurs. This violates the requirement to handle edge
cases gracefully and can cause downstream errors when code expects a list.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R298-306]

+        _append_asana_ticket_content(
+            tickets_content,
+            asana_tickets_to_include,
+            max_tickets,
+        )
+        if tickets_content:
       return tickets_content
except Exception as e:
Evidence
PR Compliance ID 3 requires graceful handling of edge cases. In the modified code,
extract_tickets() returns only when tickets_content is non-empty and has an exception handler
that logs but does not return a value, allowing an implicit None return.

Rule 3: Robust Error Handling
pr_agent/tools/ticket_pr_compliance_check.py[298-310]

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

## Issue description
`extract_tickets()` may implicitly return `None` (no explicit return) when no tickets are found or when an exception is raised, because it only returns inside `if tickets_content:`.
## Issue Context
Callers of `extract_tickets()` generally expect a list (possibly empty). Returning `None` can break iteration/serialization and makes behavior inconsistent across providers and edge cases.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[298-310]

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


2. Asana detection GitHub-only 🐞 Bug ≡ Correctness
Description
find_asana_tickets() is only invoked inside the isinstance(git_provider, GithubProvider) branch
of extract_tickets(), so non-GitHub providers will never include Asana references in
related_tickets. This makes Asana ticket compliance visibility silently unavailable for other
configured providers.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R153-160]

+            # Also detect Asana ticket references in the PR description
+            asana_tickets = find_asana_tickets(user_description)
+            for link in asana_tickets:
+                if link not in seen:
+                    seen.add(link)
+                    merged.append(link)
+            asana_links = [t for t in merged if t.startswith("https://app.asana.com/")]
+            github_like = [t for t in merged if not t.startswith("https://app.asana.com/")]
Evidence
The Asana scan and URL filtering are implemented only within the GitHub-provider branch of
extract_tickets(), while the repository explicitly supports multiple providers. The docs added in
this PR describe Asana detection as working without additional configuration, which conflicts with
the GitHub-only wiring.

pr_agent/tools/ticket_pr_compliance_check.py[134-170]
pr_agent/git_providers/init.py[17-27]
docs/docs/core-abilities/fetching_ticket_context.md[97-113]

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

## Issue description
Asana ticket detection is currently executed only when `extract_tickets()` is running in the `GithubProvider` code path. For other supported git providers, `extract_tickets()` never runs the Asana scan, so Asana references in PR descriptions are not included in `related_tickets`.
## Issue Context
- The repo supports multiple git providers.
- Docs state Asana detection “works out of the box”, but the current implementation only activates for GitHub.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[134-188]
### Implementation direction
- Move Asana reference extraction (`find_asana_tickets(...)`) to a provider-agnostic location in `extract_tickets()`.
- Use the provider base API (`git_provider.get_user_description()` exists on `GitProvider`) to obtain the text.
- For providers that are not GitHub/Azure, still return Asana placeholder ticket entries when Asana URLs are present (instead of returning `None` / no tickets).
- Keep GitHub issue fetching behavior unchanged; just merge in the Asana placeholder entries regardless of provider type.
- Add/adjust a unit test to cover a non-GitHub provider scenario (can be a minimal stub implementing `get_user_description()` and `get_pr_branch()`) to ensure Asana references are returned.

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


3. extract_tickets skips Asana fetching 📎 Requirement gap ≡ Correctness
Description
The new Asana handling explicitly avoids fetching any Asana task data and only adds a placeholder
entry, so Asana cannot be used as a real ticket provider end-to-end. This violates the requirement
to integrate an Asana Ticket Provider into the existing ticket fetching/compliance infrastructure.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R172-183]

+                    # Skip Asana URLs — these are external references,
+                    # included for visibility but cannot be fetched via GitHub API.
+                    if ticket.startswith("https://app.asana.com/"):
+                        tickets_content.append({
+                            "ticket_id": ticket,
+                            "ticket_url": ticket,
+                            "title": f"Asana Task: {ticket}",
+                            "body": ("Asana task referenced in PR description. "
+                                     "Fetch task details from Asana for full context."),
+                            "labels": "",
+                        })
+                        continue
Evidence
PR Compliance ID 6 requires an integrated Asana Ticket Provider that can fetch and supply Asana task
data to the compliance flow. The added code explicitly continues for Asana URLs and adds a
synthetic ticket body instead of fetching, and the docs state PR-Agent does not fetch Asana task
details.

Support Asana as a ticket provider in the ticket fetching and compliance infrastructure
pr_agent/tools/ticket_pr_compliance_check.py[150-183]
docs/docs/core-abilities/fetching_ticket_context.md[97-112]

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 current Asana support only detects Asana URLs and explicitly skips fetching task details, meaning Asana is not implemented as an integrated ticket provider.
## Issue Context
PR Compliance requires Asana to be supported as a ticket provider in the same infrastructure used for other providers, so compliance checks can consume fetched Asana ticket/task data end-to-end.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[150-183]
- docs/docs/core-abilities/fetching_ticket_context.md[97-112]

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


View more (1)
4. Unsafe dline[remove_count:] dedent ✓ Resolved 📘 Rule violation ≡ Correctness
Description
When delta_spaces < 0 in dedent_code(), the new dedent logic slices a fixed remove_count of
characters from each non-empty line (dline[remove_count:]) without verifying they are indentation,
which can delete real code on lines with less leading whitespace or mixed tabs/spaces. This can
corrupt suggested patches and produce invalid or behavior-changing code suggestions.
Code

pr_agent/tools/pr_code_suggestions.py[R616-626]

+                elif delta_spaces < 0:
+                    # Remove exactly -delta_spaces leading spaces from each
+                    # non-empty line.  textwrap.dedent() strips the *common*
+                    # indent which may remove too much when lines have
+                    # varying indentation levels (Qodo bug #3).
+                    remove_count = -delta_spaces
+                    dedented_lines = []
+                    for dline in new_code_snippet.split('\n'):
+                        if dline.strip():
+                            dedented_lines.append(dline[remove_count:])
+                        else:
Evidence
PR Compliance ID 3 requires anticipating and handling edge cases, and the cited negative-delta
branch removes the first remove_count characters of each non-empty line unconditionally via
dline[remove_count:], rather than stripping only leading whitespace. Because delta_spaces is
computed using lstrip()-based character counts (where tabs count as 1 character), remove_count
can exceed the actual leading indentation on some lines (especially with varying indentation or
mixed tabs/spaces), causing the slice to remove non-indentation characters and thus delete real code
content.

Rule 3: Robust Error Handling
pr_agent/tools/pr_code_suggestions.py[616-626]
pr_agent/tools/pr_code_suggestions.py[607-648]

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

## Issue description
`dedent_code()` handles `delta_spaces < 0` by slicing `dline[remove_count:]` for each non-empty line, which removes raw characters rather than only indentation; if a line has fewer than `remove_count` leading whitespace characters (or uses tabs/spaces inconsistently), this can strip real code and corrupt the resulting suggestion.
## Issue Context
This logic runs on generated/published code suggestions, so incorrect slicing can produce syntactically broken or behavior-changing patches and mislead users into applying invalid edits. A concrete failure case: original indentation starts with `\t` (so `original_initial_spaces == 1`), the suggested first line uses 4 spaces (so `suggested_initial_spaces == 4`), yielding `delta_spaces = -3` and `remove_count = 3`, and a later suggested line like `"  foo()"` (2 leading spaces) becomes `"o()"` after slicing.
## Fix Focus Areas
- pr_agent/tools/pr_code_suggestions.py[607-648]

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



Remediation recommended

5. Asana slots underfilled 🐞 Bug ☼ Reliability
Description
In extract_tickets(), when the ticket list is truncated, asana_tickets_to_include is sliced
before GitHub issues are fetched; if any GitHub issue fetch fails and is skipped, the function can
return fewer than max_related_tickets despite having additional Asana tickets available. This
makes ticket context size non-deterministically smaller on error paths and defeats the intent of the
cap as a “fill up to N” bound.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R198-210]

+            total_tickets = len(merged) + len(asana_tickets)
+            if total_tickets > max_tickets:
+                get_logger().info(
+                    f"Too many tickets (description + branch + Asana): {total_tickets}"
+                )
+                # Reserve at least one slot for an Asana reference when
+                # present so it is not systematically dropped.
+                reserved_asana_slots = 1 if asana_tickets else 0
+                github_slots = max_tickets - reserved_asana_slots
+                tickets = merged[:github_slots]
+                asana_tickets_to_include = asana_tickets[
+                    : max_tickets - len(tickets)
+                ]
Evidence
The truncation logic slices asana_tickets_to_include based on the intended GitHub ticket count,
but later GitHub fetch failures are explicitly skipped with continue, reducing tickets_content
without any subsequent re-computation of the Asana slice. Because _append_asana_ticket_content()
only receives the already-sliced list, it cannot backfill the remaining available slots up to
max_tickets.

pr_agent/tools/ticket_pr_compliance_check.py[198-210]
pr_agent/tools/ticket_pr_compliance_check.py[219-224]
pr_agent/tools/ticket_pr_compliance_check.py[298-303]

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

## Issue description
When `total_tickets > max_tickets`, `asana_tickets_to_include` is pre-sliced based on the *planned* number of GitHub tickets. If one or more GitHub issues fail to fetch (caught and `continue`), `tickets_content` ends up shorter, but the code never expands the Asana slice to fill the remaining capacity up to `max_tickets`.
## Issue Context
- GitHub issue fetch failures are expected to be handled gracefully (they’re caught and skipped), but we still want to return up to `max_related_tickets` items when more Asana refs are available.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[198-210]
- pr_agent/tools/ticket_pr_compliance_check.py[216-224]
- pr_agent/tools/ticket_pr_compliance_check.py[298-303]
## Suggested approach
- In the truncation branch, decide how many GitHub tickets to attempt (`tickets = merged[:github_slots]`), but *defer* slicing Asana until just before `_append_asana_ticket_content`.
- After the GitHub fetch loop, compute remaining capacity based on `len(tickets_content)` (actual successful GitHub fetches), then pass an Asana list that can fill the remaining slots (while still deduping by URL).

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


6. Hardcoded MAX_TICKETS = 3 📘 Rule violation ⚙ Maintainability
Description
extract_tickets() hardcodes the ticket cap via MAX_TICKETS = 3, making runtime behavior
non-configurable and harder to adjust without code changes. This conflicts with the requirement to
keep such behavioral limits in .pr_agent.toml / pr_agent/settings overrides.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R162-163]

MAX_TICKET_CHARACTERS = 10000
+    MAX_TICKETS = 3
Evidence
The checklist requires avoiding hardcoded configuration values. The new/modified code
introduces/uses a hardcoded MAX_TICKETS constant that directly controls runtime truncation
behavior instead of loading it from settings.

AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides: AGENTS.md: Do Not Hardcode Configuration Values; Use .pr_agent.toml or pr_agent/settings Overrides
pr_agent/tools/ticket_pr_compliance_check.py[161-197]

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

## Issue description
`extract_tickets()` hardcodes the max number of tickets (`MAX_TICKETS = 3`). This is runtime behavior that should be configurable via `.pr_agent.toml` or `pr_agent/settings/*` rather than embedded in code.
## Issue Context
The code already uses `get_settings()` for related ticket behavior (e.g., branch extraction), so the max ticket cap should follow the same pattern to avoid requiring code changes for operational tuning.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[161-197]
- pr_agent/settings/configuration.toml[77-93]

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


7. Single-quoted Asana regex string 📘 Rule violation ⚙ Maintainability
Description
The new Asana URL regex uses single quotes, conflicting with the project requirement to use double
quotes for Python strings. This may introduce Ruff/style inconsistencies across the codebase.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R38-40]

+_ASANA_TASK_URL_PATTERN = re.compile(
+    r'https://app\.asana\.com/0/(\d+)/(\d+)'
+)
Evidence
The compliance checklist explicitly requires double quotes for Python strings. The added regex
pattern is defined with a single-quoted raw string literal.

AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes): AGENTS.md: Python Code Must Conform to Ruff/Isort Style (120 Char Lines, Import Ordering, Double Quotes)
pr_agent/tools/ticket_pr_compliance_check.py[38-40]

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 newly added `_ASANA_TASK_URL_PATTERN` uses a single-quoted raw string. The compliance checklist requires double quotes for Python strings.
## Issue Context
This is a style consistency requirement (Ruff/isort conventions per checklist) and applies to newly introduced code.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[38-40]

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


View more (3)
8. Azure cap bypass 🐞 Bug ➹ Performance
Description
In extract_tickets(), Azure DevOps work items are only truncated when Asana tickets are present,
so Azure-only PRs can return more than MAX_TICKETS items. This can inflate related_tickets
because ticket bodies are injected into prompts verbatim, increasing request size/latency/cost.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R282-284]

+            if asana_tickets and len(tickets_content) >= MAX_TICKETS:
+                tickets_content = tickets_content[: MAX_TICKETS - 1]
+
Evidence
The Azure DevOps branch appends all linked work items to tickets_content and only truncates when
asana_tickets is non-empty, leaving Azure-only runs uncapped. The prompt template loops over
related_tickets and includes each ticket's title/labels/body, so larger tickets_content directly
increases the prompt payload.

pr_agent/tools/ticket_pr_compliance_check.py[259-291]
pr_agent/settings/pr_description_prompts.toml[96-112]

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

## Issue description
`extract_tickets()` defines `MAX_TICKETS = 3`, but the Azure DevOps branch only applies truncation when `asana_tickets` is non-empty. This means Azure-only PRs can yield an arbitrarily large `tickets_content` list (relative to `MAX_TICKETS`), which is later embedded into LLM prompts.
### Issue Context
The prompt templates render each `related_tickets` entry (including `ticket.body`) into the user prompt, so returning many Azure work items increases prompt size.
### Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[259-284]
- pr_agent/settings/pr_description_prompts.toml[96-112]
### Suggested fix
- Enforce `MAX_TICKETS` for Azure work items regardless of Asana presence (e.g., truncate `tickets_info` iteration or slice `tickets_content` after building it).
- If Asana tickets exist, optionally reserve one slot (current behavior), but still cap total to `MAX_TICKETS`.
- Add an info log when truncation occurs so operators can see when work items are being dropped.

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


9. Broad catch logs raw e 📘 Rule violation ⛨ Security
Description
extract_tickets() catches a broad Exception when reading the PR description and logs the raw
exception string, which can leak sensitive details and masks failures by silently continuing with an
empty description. This weakens security boundaries around logs and makes ticket detection behavior
depend on hidden errors.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R154-160]

+        user_description = ""
+        try:
  user_description = git_provider.get_user_description()
+        except Exception as e:
+            get_logger().warning(
+                f"Failed to get PR description for Asana ticket detection: {e}"
+            )
Evidence
PR Compliance ID 21 requires avoiding broad exception masking and preventing secret-bearing values
from being written to logs. The added code catches Exception and logs {e} directly, then
continues with user_description = "", which can hide errors and potentially leak sensitive
exception contents to logs.

pr_agent/tools/ticket_pr_compliance_check.py[154-160]
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
`extract_tickets()` uses a broad `except Exception` around `git_provider.get_user_description()` and logs the raw exception message via an f-string. This can both mask real failures (by proceeding with an empty description) and risk logging sensitive information carried in exception messages.
## Issue Context
Compliance requires treating logs as a security boundary and avoiding broad exception masking in sensitive/config-driven paths.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[154-160]

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


10. Unbounded Asana tickets 🐞 Bug ➹ Performance
Description
In extract_tickets(), the non-GitHub fallback returns all detected Asana URLs and the Azure DevOps
branch appends all Asana URLs without any cap, which can bloat related_tickets and increase
prompt/token usage or latency when PR descriptions contain many Asana links.
Code

pr_agent/tools/ticket_pr_compliance_check.py[R285-293]

+            seen_ticket_urls = {ticket.get("ticket_url") for ticket in tickets_content}
+            for ticket in asana_tickets:
+                if ticket not in seen_ticket_urls:
+                    tickets_content.append(_make_asana_ticket_content(ticket))
  return tickets_content
+        if asana_tickets:
+            return [_make_asana_ticket_content(ticket) for ticket in asana_tickets]
+
Evidence
The GitHub provider path caps the final ticket list to 3, but the Azure DevOps path appends all
detected Asana URLs and the non-GitHub fallback returns all Asana URLs without truncation, creating
inconsistent and potentially unbounded ticket context size.

pr_agent/tools/ticket_pr_compliance_check.py[184-193]
pr_agent/tools/ticket_pr_compliance_check.py[261-293]

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

## Issue description
`extract_tickets()` limits GitHub-derived tickets to 3, but the new Asana handling can return/append an unbounded number of Asana URLs for (1) Azure DevOps and (2) any other non-GitHub provider path. This can bloat `related_tickets` and increase prompt/token usage.
## Issue Context
GitHub path already enforces a max of 3 tickets. The Asana code paths should apply a similar limit (or a dedicated configurable limit) to keep behavior consistent and avoid unexpected context growth.
## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[184-193]
- pr_agent/tools/ticket_pr_compliance_check.py[261-293]

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


Grey Divider

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

Qodo Logo

Comment thread pr_agent/tools/ticket_pr_compliance_check.py Outdated
Comment thread pr_agent/tools/ticket_pr_compliance_check.py Outdated
Comment thread pr_agent/tools/pr_code_suggestions.py Outdated
@naorpeled

Copy link
Copy Markdown
Member

Hey @Oxygen56,
Thanks for working on this!

Can you please add tests to validate the new functionality? 🙏

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

qodo-free-for-open-source-projects Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit a2bf77a

Comment thread pr_agent/tools/ticket_pr_compliance_check.py Fixed
Comment thread pr_agent/tools/ticket_pr_compliance_check.py Fixed
Comment thread pr_agent/tools/ticket_pr_compliance_check.py Fixed
Comment thread pr_agent/tools/ticket_pr_compliance_check.py Outdated
Comment thread pr_agent/tools/ticket_pr_compliance_check.py Outdated
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

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

qodo-free-for-open-source-projects Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit a0c62b4

Comment thread pr_agent/tools/pr_code_suggestions.py Outdated
Comment thread tests/unittest/test_ticket_compliance.py Outdated
@Oxygen56

Oxygen56 commented Jun 6, 2026

Copy link
Copy Markdown
Author

感谢所有 review 意见,我已在最新提交中处理了以下问题:

Bug 修复:

  • ✅ 不安全的 dline[remove_count:] 去缩进 → 改为只剥离每行实际的空白字符
  • ✅ Asana ticket 被截断丢弃 → 重构截断逻辑,存在 Asana ticket 时至少保留 1 个位置
  • ✅ Asana 缩写大小写敏感 → 添加 re.IGNORECASE,现在支持 ASANA-/asana-/Asana-
  • ✅ Asana 缩写过度匹配 → 添加 \b 单词边界

规范/安全修复:

  • ✅ 移除未使用的 import pytest
  • _ASANA_TASK_*_PATTERN 常量现在在 find_asana_tickets() 中使用
  • ✅ 单引号改为双引号
  • ✅ CodeQL: 使用 startswith() 替代子串匹配

测试补充:

  • ✅ 新增 test_shorthand_mixed_case_asana 测试
  • ✅ 新增 test_shorthand_respects_word_boundary 测试

Comment thread tests/unittest/test_ticket_compliance.py Fixed
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

Oxygen56 and others added 6 commits June 8, 2026 23:27
The dedent_code method only adjusted indentation when delta_spaces > 0
and never normalized the indentation character. This caused /improve to
replace tabs with spaces in Go, Makefile, and other tab-indented codebases.

Changes:
- Handle delta_spaces < 0 case with dedent (not just > 0)
- Normalize all suggestion lines to use the original file's indentation
  character (tab or space), auto-detected from the existing code

Fixes The-PR-Agent#1858

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds support for detecting Asana task references in PR descriptions
and branch names. Supports both full URLs and ASANA-123456789 shorthand
format.

- find_asana_tickets() extracts Asana task URLs from text
- Asana tickets are included in the compliance check's related tickets
- Graceful handling: Asana URLs are shown with a placeholder body since
  they cannot be fetched via the GitHub API

Fixes The-PR-Agent#2002

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Use ticket_url instead of url (templates access ticket.ticket_url)
- Add labels field (templates access ticket.labels under StrictUndefined)
- Add ticket_id field for consistency with GitHub ticket entries
- Replace textwrap.dedent() with exact -delta_spaces removal to avoid
  stripping too much indentation when lines have varying levels
- Preserve remainder spaces when converting spaces to tabs
  (e.g. 6 spaces → 1 tab + 2 spaces instead of silently dropping 2)
…check

- Covers full Asana URLs, shorthand ASANA- prefix, case-insensitivity
- Tests deduplication, sorting, empty input, and mixed PR description content
- Validates GitHub URLs are not misidentified as Asana tickets
- Use compiled Asana patterns (_ASANA_TASK_URL_PATTERN,
  _ASANA_TASK_SHORT_PATTERN) in find_asana_tickets() instead of
  inline regex (CodeQL unused-variable, comments The-PR-Agent#5/The-PR-Agent#6).
- Add re.IGNORECASE flag to _ASANA_TASK_SHORT_PATTERN for proper
  case-insensitive matching.
- Replace substring match ('app.asana.com' in ticket) with
  prefix check (ticket.startswith()) to fix CodeQL URL
  sanitization warning (comment The-PR-Agent#4).
- Reserve at least one slot for Asana tickets during
  truncation when there are 3+ GitHub/branch tickets
  (comment The-PR-Agent#8).
- Dedent only actual leading whitespace instead of blindly
  slicing dline[remove_count:] to avoid deleting real code
  on lines with less indentation (comments The-PR-Agent#3/The-PR-Agent#9).
- Remove unused 'import pytest' from test file
  (comments The-PR-Agent#10/The-PR-Agent#20).
- Add test_shorthand_mixed_case_asana and
  test_shorthand_respects_word_boundary test cases.
@Oxygen56 Oxygen56 force-pushed the feat/asana-ticket-provider branch from 2921c2b to 3ce5450 Compare June 8, 2026 15:29
@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 3ce5450

@Oxygen56

Oxygen56 commented Jun 8, 2026

Copy link
Copy Markdown
Author

Hey @naorpeled, thanks for the review!

Tests have been added — there are now 12 test cases in tests/unittest/test_ticket_compliance.py covering:

  • Full Asana URL detection (https://app.asana.com/0/...)
  • Shorthand format (ASANA-123456789012)
  • Case-insensitive matching (asana-, Asana-)
  • Word boundary enforcement (no false matches on NOTASANA-...)
  • Deduplication of identical tickets
  • Multiple tickets in mixed content
  • Empty input / no-ticket text
  • GitHub URL exclusion
  • Sorted output

The test file was added in commit a0c62b4 and extended with two additional edge cases in the latest push (3ce5450). Let me know if anything else is needed!

Comment thread pr_agent/tools/pr_code_suggestions.py Outdated
@dellch

dellch commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

There are docs (docs/docs/core-abilities/fetching_ticket_context.md) that perhaps should be updated as well?

…ode_suggestions.py, update docs with Asana support
@Oxygen56

Copy link
Copy Markdown
Author

Updated docs/docs/core-abilities/fetching_ticket_context.md with an Asana Integration section covering supported reference formats (full URLs and ASANA-123456789012 shorthand), how to link a PR to an Asana task, and a note that Asana task content is not fetched via API (reference-only for compliance checking).

@Oxygen56

Copy link
Copy Markdown
Author

Tests for the Asana ticket detection are already included in this PR — see tests/unittest/test_ticket_compliance.py, which was added with 96 lines covering both full Asana URL and shorthand format detection.

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

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 0573be2

Comment thread pr_agent/tools/ticket_pr_compliance_check.py Outdated
Per maintainer feedback (naorpeled), remove the invented ASANA- prefix
shorthand since Asana has no native shorthand format like Jira's PROJ-123.
Only full Asana URLs are now supported.

- Remove _ASANA_TASK_SHORT_PATTERN regex and matching loop
- Update find_asana_tickets() docstring
- Update docs to remove shorthand format references
- Update tests to use full URLs only, drop shorthand-specific cases
@qodo-free-for-open-source-projects

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 4caee4c

@naorpeled

Copy link
Copy Markdown
Member

Hey @Oxygen56,
this overall LGTM, can you please address the last comments by Qodo?

Thanks!

Add extract_tickets coverage for Asana references, including the capped-ticket path that reserves a slot for an Asana URL.
@Oxygen56

Oxygen56 commented Jul 8, 2026

Copy link
Copy Markdown
Author

Thanks for the nudge. I addressed the remaining Qodo items in the latest push:

  • The unused pytest import is no longer present in tests/unittest/test_ticket_compliance.py.
  • Added integration coverage for extract_tickets() so Asana URLs are validated in the merged ticket-content path, not only in the standalone parser.
  • Added coverage for the capped-ticket path to ensure an Asana URL is still retained when the related ticket list exceeds the 3-ticket limit.

Validation run locally:

  • .venv/bin/python -m pytest tests/unittest/test_ticket_compliance.py -q -> 10 passed
  • .venv/bin/ruff check tests/unittest/test_ticket_compliance.py -> passed
  • .venv/bin/ruff check --select F401 tests/unittest/test_ticket_compliance.py -> passed
  • git diff --check -> passed

Comment on lines +172 to +183
# Skip Asana URLs — these are external references,
# included for visibility but cannot be fetched via GitHub API.
if ticket.startswith("https://app.asana.com/"):
tickets_content.append({
"ticket_id": ticket,
"ticket_url": ticket,
"title": f"Asana Task: {ticket}",
"body": ("Asana task referenced in PR description. "
"Fetch task details from Asana for full context."),
"labels": "",
})
continue

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. extract_tickets skips asana fetching 📎 Requirement gap ≡ Correctness

The new Asana handling explicitly avoids fetching any Asana task data and only adds a placeholder
entry, so Asana cannot be used as a real ticket provider end-to-end. This violates the requirement
to integrate an Asana Ticket Provider into the existing ticket fetching/compliance infrastructure.
Agent Prompt
## Issue description
The current Asana support only detects Asana URLs and explicitly skips fetching task details, meaning Asana is not implemented as an integrated ticket provider.

## Issue Context
PR Compliance requires Asana to be supported as a ticket provider in the same infrastructure used for other providers, so compliance checks can consume fetched Asana ticket/task data end-to-end.

## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[150-183]
- docs/docs/core-abilities/fetching_ticket_context.md[97-112]

ⓘ 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

Code review by qodo was updated up to the latest commit 65460ce

@Oxygen56

Oxygen56 commented Jul 8, 2026

Copy link
Copy Markdown
Author

Thanks, I pushed bc043c7 to address the actionable new Qodo items:

  • find_asana_tickets() now returns [] for None, non-string, and empty input, so extract_tickets() no longer reaches regex matching with invalid text.
  • The 3-ticket truncation path now backfills from available Asana links while still preserving at least one Asana reference when present.
  • Added tests for None input and the all-Asana truncation/backfill case.

Validation:

  • .venv/bin/python -m pytest tests/unittest/test_ticket_compliance.py -q -> 12 passed
  • .venv/bin/ruff check tests/unittest/test_ticket_compliance.py -> passed
  • .venv/bin/python -m py_compile pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • git diff --check -> passed

On the broader Asana fetching note: this PR currently keeps Asana references as detection/reference-only, matching the docs added here. Implementing full Asana API fetching would require new provider config/auth handling, so I am keeping that out of scope unless you want this PR expanded in that direction.

Comment on lines +153 to +160
# Also detect Asana ticket references in the PR description
asana_tickets = find_asana_tickets(user_description)
for link in asana_tickets:
if link not in seen:
seen.add(link)
merged.append(link)
asana_links = [t for t in merged if t.startswith("https://app.asana.com/")]
github_like = [t for t in merged if not t.startswith("https://app.asana.com/")]

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. Asana detection github-only 🐞 Bug ≡ Correctness

find_asana_tickets() is only invoked inside the isinstance(git_provider, GithubProvider) branch
of extract_tickets(), so non-GitHub providers will never include Asana references in
related_tickets. This makes Asana ticket compliance visibility silently unavailable for other
configured providers.
Agent Prompt
## Issue description
Asana ticket detection is currently executed only when `extract_tickets()` is running in the `GithubProvider` code path. For other supported git providers, `extract_tickets()` never runs the Asana scan, so Asana references in PR descriptions are not included in `related_tickets`.

## Issue Context
- The repo supports multiple git providers.
- Docs state Asana detection “works out of the box”, but the current implementation only activates for GitHub.

## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[134-188]

### Implementation direction
- Move Asana reference extraction (`find_asana_tickets(...)`) to a provider-agnostic location in `extract_tickets()`.
  - Use the provider base API (`git_provider.get_user_description()` exists on `GitProvider`) to obtain the text.
- For providers that are not GitHub/Azure, still return Asana placeholder ticket entries when Asana URLs are present (instead of returning `None` / no tickets).
- Keep GitHub issue fetching behavior unchanged; just merge in the Asana placeholder entries regardless of provider type.
- Add/adjust a unit test to cover a non-GitHub provider scenario (can be a minimal stub implementing `get_user_description()` and `get_pr_branch()`) to ensure Asana references are returned.

ⓘ 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

Code review by qodo was updated up to the latest commit bc043c7

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

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit a096303

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

qodo-free-for-open-source-projects Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@Oxygen56

Oxygen56 commented Jul 9, 2026

Copy link
Copy Markdown
Author

Pushed cf17141 for the latest Qodo pass:

  • Removed the inner broad catch around get_user_description() so failures are no longer silently converted to an empty description by the new Asana path.
  • Added a shared cap for provider-agnostic Asana ticket entries so non-GitHub fallback and Azure paths cannot add unbounded Asana references.
  • Azure now preserves one slot for an Asana reference when the work-item list is already at the 3-ticket cap.
  • Added tests for non-GitHub Asana capping and Azure + Asana capping.

Validation:

  • .venv/bin/python -m pytest tests/unittest/test_ticket_compliance.py -q -> 15 passed
  • .venv/bin/ruff check tests/unittest/test_ticket_compliance.py -> passed
  • .venv/bin/python -m py_compile pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • git diff --check -> passed

The remaining full Asana API fetching/provider-auth item is still a scope question rather than a small follow-up fix.

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

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit cf17141

@Oxygen56

Oxygen56 commented Jul 9, 2026

Copy link
Copy Markdown
Author

Pushed 1c1a693 for the latest Qodo pass:

  • max_related_tickets is now configurable via pr_reviewer.max_related_tickets with a default of 3.
  • The Asana URL regex now follows the project double-quote style.
  • Azure DevOps linked work items are now capped even when no Asana references are present, while still reserving one slot for Asana when applicable.
  • Added tests for configurable ticket caps and Azure-only capping.

Validation:

  • .venv/bin/python -m pytest tests/unittest/test_ticket_compliance.py -q -> 17 passed
  • .venv/bin/ruff check pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • .venv/bin/python -m py_compile pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • git diff --check -> passed

A couple of remaining Qodo entries look stale or scope-level: non-GitHub Asana detection is already provider-agnostic and covered by test_extract_tickets_includes_asana_for_non_github_provider; the full Asana API fetching/provider-auth item would require a broader config/auth provider implementation beyond this reference-detection PR.

Comment on lines +298 to 306
_append_asana_ticket_content(
tickets_content,
asana_tickets_to_include,
max_tickets,
)
if tickets_content:
return tickets_content

except Exception as e:

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. extract_tickets() can return none 📘 Rule violation ☼ Reliability

extract_tickets() only returns when tickets_content is truthy, so callers can receive None
when no tickets are found or when an exception occurs. This violates the requirement to handle edge
cases gracefully and can cause downstream errors when code expects a list.
Agent Prompt
## Issue description
`extract_tickets()` may implicitly return `None` (no explicit return) when no tickets are found or when an exception is raised, because it only returns inside `if tickets_content:`.

## Issue Context
Callers of `extract_tickets()` generally expect a list (possibly empty). Returning `None` can break iteration/serialization and makes behavior inconsistent across providers and edge cases.

## Fix Focus Areas
- pr_agent/tools/ticket_pr_compliance_check.py[298-310]

ⓘ 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

Code review by qodo was updated up to the latest commit 1c1a693

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

qodo-free-for-open-source-projects Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

Grey Divider

New Review Started

This review has been superseded by a new analysis

Grey Divider

Qodo Logo

@Oxygen56

Oxygen56 commented Jul 9, 2026

Copy link
Copy Markdown
Author

Follow-up pushed as c928e6c for the same extract_tickets() return-path item:

  • The success path now returns tickets_content unconditionally, so no-ticket cases return [] directly.
  • The exception path still returns [].

Validation remains green:

  • .venv/bin/python -m pytest tests/unittest/test_ticket_compliance.py -q -> 19 passed
  • .venv/bin/ruff check pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • .venv/bin/python -m py_compile pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • git diff --check -> passed

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

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit c928e6c

@Oxygen56

Oxygen56 commented Jul 9, 2026

Copy link
Copy Markdown
Author

Pushed 557b28c for the new Qodo Asana slots underfilled item:

  • The GitHub path now keeps the full Asana candidate list available for the shared append helper.
  • If a GitHub issue fetch is skipped after truncation, Asana references can still backfill the remaining slots up to max_related_tickets.
  • Added regression coverage for a GitHub issue fetch failure followed by Asana backfill.

Validation:

  • .venv/bin/python -m pytest tests/unittest/test_ticket_compliance.py -q -> 20 passed
  • .venv/bin/ruff check pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • .venv/bin/python -m py_compile pr_agent/tools/ticket_pr_compliance_check.py tests/unittest/test_ticket_compliance.py -> passed
  • git diff --check -> passed

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

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 557b28c

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Support for Asana Ticket Provider

4 participants