Skip to content

Rebase consolidation improvements and code review fixes - #726

Open
majamassarini wants to merge 15 commits into
packit:mainfrom
majamassarini:group-rebases
Open

Rebase consolidation improvements and code review fixes#726
majamassarini wants to merge 15 commits into
packit:mainfrom
majamassarini:group-rebases

Conversation

@majamassarini

Copy link
Copy Markdown
Member

Summary

This PR implements rebase consolidation (grouping multiple CVE issues into a single rebase MR) and addresses code review findings for performance and maintainability.

Key Features

Rebase Consolidation

  • Group sibling Jira issues requiring the same package rebase into a single MR
  • Link consolidated siblings to primary issue during triage and on failures
  • Prevent circular consolidation by excluding already-triaged issues
  • Handle "already at target version" scenarios gracefully

Code Review Fixes

  1. Semantic version comparison - Use rpmdev-vercmp instead of string equality for sibling version matching
  2. Parallelization - Use asyncio.gather() for sibling analysis and Jira API calls
  3. Eliminate duplication - Extract shared utilities for JQL building and label updates

Changes

Consolidation Implementation

  • ymir/agents/rebase_consolidation.py: Find and analyze sibling issues for consolidation
  • ymir/agents/triage_agent.py: Link siblings during triage, handle NOT_AFFECTED for already-rebased packages
  • ymir/agents/rebase_agent.py: Post comments/labels to all consolidated issues, helper for failure notifications
  • ymir/agents/prompts/triage/prompt.j2: Check current version before deciding REBASE resolution

Performance & Maintainability

  • Parallelization: Sibling analysis and Jira updates now run concurrently
  • Shared utilities: build_siblings_jql() for JQL construction, update_labels_for_all_issues() for label updates
  • Semantic comparison: compare_versions() in version_utils.py wraps rpmdev-vercmp

Testing

Tested against dotnet8.0 CVE issues (RHEL-211859 and 12 siblings):

  • Consolidated into single rebase MR
  • All siblings labeled and linked correctly
  • "Already at version" detection works (returns NOT_AFFECTED with link to existing rebase)

Related Issues

Addresses feedback from rebase consolidation implementation review and Slack discussion about RHEL-211859 "already at version" error.

🤖 Generated with Claude Code

@qodo-for-packit

Copy link
Copy Markdown

PR Summary by Qodo

Consolidate rebase MRs across sibling Jira issues + version-aware triage fixes

✨ Enhancement 🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Consolidate sibling CVE Jira issues into a single rebase MR when target versions match.
• Propagate MR links, Jira comments, and labels across all consolidated issues (success/failure).
• Improve triage/rebase prompting to detect “already at target version” and avoid redundant rebases.
Diagram

graph TD
  A["Triage agent"] --> B["Rebase consolidation"] --> C["RebaseData (consolidated list)"] --> D["Rebase agent"] --> E["MR creation"] --> F["Jira updates"]
  B --> G["Version utils (rpmdev-vercmp)"]
  subgraph Legend
    direction LR
    _a["Agent/workflow"] ~~~ _m["Model"] ~~~ _u["Utility"]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Deterministic sibling matching (no LLM)
  • ➕ More predictable behavior and lower runtime cost
  • ➕ Easier to test and reason about matching rules
  • ➖ Harder to reliably extract target versions across varied Jira text/comments
  • ➖ May reduce consolidation rate vs. LLM-based interpretation
2. Persist consolidation via Jira links/fields instead of labels+comments
  • ➕ Richer traceability in Jira (relationship is queryable)
  • ➕ Less reliance on comment text conventions
  • ➖ Requires Jira schema/permission changes and more complex writes
  • ➖ May not be available in all projects/environments
3. Centralize fan-out side effects in a shared helper module
  • ➕ Avoids duplicating parallel comment/label patterns across agents
  • ➕ Makes future consolidation types cheaper to add
  • ➖ Refactor scope increases and may slow down delivery of current feature

Recommendation: The chosen approach (LLM verification + explicit eligibility gating + RebaseData propagation) is a good fit for heterogeneous Jira content and matches the existing rebuild consolidation pattern. If runtime/cost becomes an issue, consider a hybrid: deterministic pre-filtering (e.g., summary regex) followed by LLM only for ambiguous candidates.

Files changed (10) +591 / -40

Enhancement (5) +491 / -24
rebase_agent.pyFan out MR/Jira updates to consolidated rebase siblings +114/-23

Fan out MR/Jira updates to consolidated rebase siblings

• Extends the rebase workflow state to carry consolidated issues and a consolidation summary. MR descriptions now include links to all related Jira issues, and Jira comments/labels are applied across the group (parallelized with asyncio.gather). Failure handling posts detailed errors to the primary issue and link-style notifications to siblings.

ymir/agents/rebase_agent.py

rebase_consolidation.pyNew module to discover and validate rebase sibling issues +258/-0

New module to discover and validate rebase sibling issues

• Introduces a rebase consolidation workflow: build JQL to find sibling candidates, gate on CVE triage eligibility, and use an LLM to confirm the sibling requires the exact same rebase target. Uses rpmdev-vercmp-backed comparison to avoid string-equality pitfalls and analyzes candidates concurrently.

ymir/agents/rebase_consolidation.py

triage_agent.pyIntegrate rebase consolidation step into triage workflow +67/-1

Integrate rebase consolidation step into triage workflow

• Adds a consolidate_rebase_siblings workflow step and routes REBASE resolutions through it (including after applicability checks). On successful triage, consolidated siblings are labeled to prevent re-triage and receive link comments pointing to the primary issue.

ymir/agents/triage_agent.py

models.pyExtend RebaseData with consolidation metadata and helper property +13/-0

Extend RebaseData with consolidation metadata and helper property

• Adds consolidated_issues and consolidation_summary fields to RebaseData, plus an all_jira_issues helper for primary + siblings. This enables downstream agents to treat grouped rebases as a first-class model concern.

ymir/common/models.py

version_utils.pyAdd rpmdev-vercmp wrapper for semantic version comparison +39/-0

Add rpmdev-vercmp wrapper for semantic version comparison

• Introduces compare_versions() to compare upstream versions using rpmdev-vercmp exit codes, raising clear errors when the tool is missing or fails. This is used by consolidation logic to avoid incorrect string-based matching.

ymir/common/version_utils.py

Bug fix (2) +23 / -1
instructions.j2Treat “already at target version” as a graceful rebase outcome +4/-1

Treat “already at target version” as a graceful rebase outcome

• Updates rebase instructions to avoid hard errors when the package is already at (or newer than) the target version. The prompt now asks the agent to return a failure with actionable guidance (search for existing builds and attach to Errata).

ymir/agents/prompts/rebase/instructions.j2

prompt.j2Add mandatory “already at target version” check before choosing REBASE +19/-0

Add mandatory “already at target version” check before choosing REBASE

• Extends triage decision guidance to require checking the dist-git spec version and comparing via rpmdev-vercmp before deciding on a rebase. If already rebased, the agent is instructed to return NOT_AFFECTED and reference the existing rebase/build when possible.

ymir/agents/prompts/triage/prompt.j2

Refactor (1) +12 / -15
rebuild_consolidation.pyReuse shared sibling JQL builder for rebuild consolidation +12/-15

Reuse shared sibling JQL builder for rebuild consolidation

• Refactors rebuild sibling discovery to call the new shared build_siblings_jql helper and reuse centralized excluded-label lists. Removes duplicated fixVersion-variant handling from this module.

ymir/agents/rebuild_consolidation.py

Tests (2) +65 / -0
test_rebase_consolidation.pyAdd unit tests for rebase sibling JQL construction +34/-0

Add unit tests for rebase sibling JQL construction

• Adds coverage for fixVersion variants, component escaping, and excluded-label behavior in build_rebase_siblings_jql. Ensures consolidation queries don’t accidentally exclude/include incorrect triage states.

ymir/agents/tests/unit/test_rebase_consolidation.py

test_models.pyTest RebaseData.all_jira_issues behavior +31/-0

Test RebaseData.all_jira_issues behavior

• Adds unit tests validating all_jira_issues returns only the primary key when no consolidation exists, and includes sibling keys when provided. Confirms expected ordering and non-destructive behavior of base fields.

ymir/common/tests/unit/test_models.py

@qodo-for-packit

qodo-for-packit Bot commented Jul 31, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 7 rules

Grey Divider


Action required

1. Sibling failure comments skipped ✓ Resolved 🐞 Bug ≡ Correctness
Description
post_failure_comments_to_consolidated_siblings() calls `tasks.comment_in_jira(...,
is_error=True), which is suppressed when user_triggered=False`, so consolidated siblings won’t
receive failure/link comments during normal automated runs. This undermines the “link siblings on
failures” behavior (siblings only get failure context when runs are ymir_todo-triggered).
Code

ymir/agents/rebase_agent.py[R218-225]

+                await tasks.comment_in_jira(
+                    jira_issue=consolidated.issue_key,
+                    agent_type="Rebase",
+                    comment_text=f"Consolidated rebase failed. See {primary_issue} for error details.",
+                    available_tools=available_tools,
+                    is_error=True,
+                    user_triggered=user_triggered,
+                )
Relevance

●●● Strong

Deterministic logic bug: is_error comments suppressed for non-user-triggered runs; breaks intended
sibling linking.

PR-#611

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helper marks sibling link comments as is_error=True, but tasks.comment_in_jira returns early
on error comments when user_triggered is false; this means the helper’s intended side-effect does
not occur in typical automated runs.

ymir/agents/rebase_agent.py[204-229]
ymir/agents/tasks.py[382-395]
ymir/agents/rebase_agent.py[472-509]

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

## Issue description
Consolidated sibling failure-link comments are posted via `tasks.comment_in_jira` with `is_error=True`, but `tasks.comment_in_jira` intentionally skips error comments unless `user_triggered=True`. As a result, on non-user-triggered (automatic) runs, consolidated sibling issues never get the failure/link comment.

## Issue Context
This helper is intended to post an informational link comment to consolidated siblings pointing to the primary issue for details, even when we suppress noisy error notifications.

## Fix Focus Areas
- ymir/agents/rebase_agent.py[216-229]

## Suggested fix
Change the sibling link comment to be non-error (e.g., `is_error=False`) so it is always posted, while keeping detailed error comments to the primary issue gated by `is_error=True`/`user_triggered` as today. If you still need special formatting for failures, consider adding a dedicated “informational failure link” helper that bypasses the error-suppression rule.

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


2. Comment fanout can crash ✓ Resolved 🐞 Bug ☼ Reliability
Description
The rebase workflow now posts Jira comments to multiple issues via asyncio.gather() without
per-issue error handling; a single Jira API failure will raise and abort the workflow. This can
trigger retries after side effects (e.g., MR already opened) and leave some consolidated issues
without status updates.
Code

ymir/agents/rebase_agent.py[R440-454]

+                    # Post same success message to all issues in parallel
+                    all_issues = [state.jira_issue] + [item.issue_key for item in state.consolidated_issues]
+                    await asyncio.gather(
+                        *[
+                            tasks.comment_in_jira(
+                                jira_issue=issue,
+                                agent_type="Rebase",
+                                comment_text=comment_text,
+                                is_error=is_error,
+                                available_tools=gateway_tools,
+                                user_triggered=user_triggered,
+                            )
+                            for issue in all_issues
+                        ]
+                    )
Relevance

●●● Strong

Team previously added per-issue try/except for Jira comment fanout to avoid aborting on one failure.

PR-#611

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
rebase_agent now uses asyncio.gather() to fan out Jira comments to multiple issues, while
tasks.comment_in_jira does not handle exceptions and will propagate failures from
run_tool(add_jira_comment). The queue-mode runner treats uncaught exceptions as retryable
failures, so comment failures can cause retries after other steps already succeeded.

ymir/agents/rebase_agent.py[432-471]
ymir/agents/tasks.py[382-403]
ymir/agents/rebase_agent.py[615-645]

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

## Issue description
`ymir/agents/rebase_agent.py` posts comments to `[primary] + consolidated` issues using `asyncio.gather(...)` on `tasks.comment_in_jira(...)`. Because `tasks.comment_in_jira()` does not catch exceptions (it directly awaits `run_tool('add_jira_comment', ...)`), any single Jira comment failure will cause `gather()` to raise and the workflow step to abort.

This is especially risky because the comment step is late in the workflow (after rebase/build/MR creation). A transient Jira failure while commenting can therefore convert an otherwise-successful run into an exception path and trigger retries.

## Issue Context
- Success path uses `asyncio.gather` for multiple comments.
- Failure path also posts to consolidated siblings without local try/except in `post_failure_comments_to_consolidated_siblings`.
- `tasks.comment_in_jira` propagates tool exceptions.

## Fix Focus Areas
- ymir/agents/rebase_agent.py[432-472]
- ymir/agents/tasks.py[382-403]
- ymir/agents/rebase_agent.py[615-645]

## What to change
- Wrap each `tasks.comment_in_jira(...)` call so one failure doesn’t abort the entire step.
 - Option A: `results = await asyncio.gather(*coros, return_exceptions=True)` and log exceptions per issue.
 - Option B: loop with per-issue `try/except` (still can be parallelized with bounded concurrency).
- Apply the same per-issue error isolation to `post_failure_comments_to_consolidated_siblings()` so a single sibling comment failure doesn’t crash the workflow.

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


3. JQL/test contradiction ✓ Resolved 🐞 Bug ≡ Correctness
Description
build_rebase_siblings_jql() excludes ymir_triaged_rebase, but the newly-added unit tests assert
that ymir_triaged_rebase must NOT appear in the JQL, which makes the tests fail and leaves
intended consolidation behavior ambiguous. This will block CI and/or ship incorrect sibling
filtering depending on which behavior is intended.
Code

ymir/agents/rebase_consolidation.py[R75-81]

+        excluded_labels=[
+            JiraLabels.TRIAGED_NOT_AFFECTED.value,
+            JiraLabels.TRIAGED_BACKPORT.value,
+            JiraLabels.TRIAGED_REBUILD.value,
+            JiraLabels.TRIAGED_REBASE.value,
+            JiraLabels.TRIAGED_POSTPONED.value,
+        ],
Relevance

●●● Strong

Deterministic test/implementation mismatch; similar consolidation JQL behavior was adjusted before
to stop excluding triaged labels.

PR-#554

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The implementation explicitly excludes TRIAGED_REBASE, while the new tests explicitly assert it
must not be excluded; since TRIAGED_REBASE equals ymir_triaged_rebase, these cannot both be
correct and will fail tests as written.

ymir/agents/rebase_consolidation.py[65-82]
ymir/agents/tests/unit/test_rebase_consolidation.py[4-34]
ymir/common/constants.py[137-165]

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

## Issue description
`build_rebase_siblings_jql()` currently adds `JiraLabels.TRIAGED_REBASE.value` to `excluded_labels`, which makes the produced JQL exclude issues already labeled `ymir_triaged_rebase`. However, the new unit tests assert that `"ymir_triaged_rebase"` is *not* present in the JQL.

This contradiction will deterministically fail the test suite and also indicates unclear desired behavior (should already-triaged-rebase issues be eligible for consolidation or excluded to avoid circular consolidation?).

## Issue Context
- `JiraLabels.TRIAGED_REBASE.value` is defined as `"ymir_triaged_rebase"`.
- Tests currently expect `"ymir_triaged_rebase"` to be absent from the JQL.

## Fix Focus Areas
- ymir/agents/rebase_consolidation.py[65-82]
- ymir/agents/tests/unit/test_rebase_consolidation.py[4-34]
- ymir/common/constants.py[137-165]

## What to change
Choose one and make code+tests consistent:
1) If preventing circular consolidation is the goal: keep excluding `TRIAGED_REBASE` and update the tests/docstrings to assert it *is* present in the `labels not in (...)` clause.
2) If triaged-rebase issues should still be considered siblings: remove `JiraLabels.TRIAGED_REBASE.value` from `excluded_labels` and keep the current tests.

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



Remediation recommended

4. Label fanout aborts on error 🐞 Bug ☼ Reliability ⭐ New
Description
In rebase_agent.update_labels_for_all_issues(), a single Jira label update failure will raise out of
asyncio.gather() and can interrupt the overall rebase processing while leaving some issues labeled
and others not. This affects success/failure/error paths that now label primary + consolidated
siblings together.
Code

ymir/agents/rebase_agent.py[R159-173]

+        """Update Jira labels for primary issue and all consolidated siblings in parallel."""
+        # Deduplicate in case consolidated_issues contains duplicates or the primary issue
+        all_issues = list(dict.fromkeys([primary_issue] + [item.issue_key for item in consolidated_issues]))
+        await asyncio.gather(
+            *[
+                tasks.set_jira_labels(
+                    jira_issue=issue,
+                    labels_to_add=labels_to_add,
+                    labels_to_remove=labels_to_remove,
+                    dry_run=dry_run,
+                    user_triggered=user_triggered,
+                )
+                for issue in all_issues
+            ]
+        )
Relevance

●●● Strong

Team has accepted per-issue Jira error isolation/dedup patterns; gather abort risk likely fixed
similarly.

PR-#611
PR-#427

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helper fans out label writes via gather with no try/except/return_exceptions, and it is invoked
in the retry exhaustion path and in both success and failure labeling paths.

ymir/agents/rebase_agent.py[151-173]
ymir/agents/rebase_agent.py[601-667]
ymir/agents/rebase_agent.py[701-731]

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

### Issue description
`update_labels_for_all_issues()` uses `asyncio.gather()` without per-issue exception isolation. If `tasks.set_jira_labels()` raises for one issue, the whole await raises, which can disrupt the rebase task and leave inconsistent labels across the primary/sibling issues.

### Issue Context
This helper is used from multiple rebase paths (final retry exhaustion, success, and failure), so it needs to be resilient to partial Jira outages.

### Fix Focus Areas
- ymir/agents/rebase_agent.py[151-173]
- ymir/agents/rebase_agent.py[618-626]
- ymir/agents/rebase_agent.py[703-714]
- ymir/agents/rebase_agent.py[723-731]

### Suggested fix
- Wrap each `tasks.set_jira_labels(...)` call in a small inner coroutine with `try/except` (like the comment fanout helper), log warnings per issue, and continue.
 - OR use `asyncio.gather(..., return_exceptions=True)` and iterate results to log failures.
- Ensure the helper never raises due to a single sibling label failure (unless you explicitly want to fail the whole workflow).

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


5. Missing target_version breaks compare 🐞 Bug ≡ Correctness ⭐ New
Description
find_rebase_siblings() calls compare_versions(analysis.target_version, ...) when
requires_same_rebase is true, but target_version is optional in the SiblingRebaseAnalysis schema. If
the LLM returns null for target_version, the comparison raises and that sibling is treated as an
analysis failure (excluded).
Code

ymir/agents/rebase_consolidation.py[R183-185]

+            if analysis.requires_same_rebase:
+                cmp_result = compare_versions(analysis.target_version, rebase_data.version)
+                if cmp_result == 0:
Relevance

●●● Strong

Null/optional schema fields causing runtime failures are typically guarded or made optional to avoid
crashes.

PR-#81

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The schema declares target_version as optional, but the compare call is unconditional inside the
requires_same_rebase branch.

ymir/agents/rebase_consolidation.py[85-95]
ymir/agents/rebase_consolidation.py[183-207]

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

### Issue description
`SiblingRebaseAnalysis.target_version` is nullable, but the code assumes it’s always present when `requires_same_rebase` is true and passes it into `compare_versions()`. A null value triggers an exception and causes an avoidable false-negative (sibling excluded).

### Issue Context
The exception is caught and the candidate is excluded, so the workflow won’t crash, but consolidation quality suffers and debugging is harder.

### Fix Focus Areas
- ymir/agents/rebase_consolidation.py[85-95]
- ymir/agents/rebase_consolidation.py[183-207]

### Suggested fix
- Add an explicit guard:
 - If `analysis.requires_same_rebase` and not `analysis.target_version`, return an exclusion summary like "missing target_version" without calling `compare_versions()`.
- Alternatively, enforce a Pydantic validator: `requires_same_rebase=True` => `target_version` must be non-null (and ideally non-empty).

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


6. Unbounded sibling analysis concurrency 🐞 Bug ➹ Performance ⭐ New
Description
find_rebase_siblings() gathers analysis for all candidates at once; with max_results=50 this can
spawn many concurrent eligibility checks and LLM/Jira calls. This can increase latency and reduce
successful consolidation under throttling or transient Jira/model issues.
Code

ymir/agents/rebase_consolidation.py[R214-215]

+    # Analyze all candidates in parallel
+    results = await asyncio.gather(*[analyze_candidate(c) for c in candidates])
Relevance

●● Moderate

Concurrency limiting is a performance/ops tradeoff; repo embraces parallelism but no clear precedent
for semaphores here.

PR-#630
PR-#657

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The search pulls up to 50 candidates and then runs analyze_candidate for all of them concurrently
via gather.

ymir/agents/rebase_consolidation.py[125-131]
ymir/agents/rebase_consolidation.py[214-229]

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

### Issue description
Sibling analysis runs with `asyncio.gather()` over all candidates, with no concurrency cap. With up to 50 siblings, this can overwhelm downstream services and increase failures/timeouts.

### Issue Context
Each candidate can trigger multiple remote operations (eligibility tool + LLM agent using Jira tools), so even moderate sibling counts can be expensive.

### Fix Focus Areas
- ymir/agents/rebase_consolidation.py[125-131]
- ymir/agents/rebase_consolidation.py[214-229]

### Suggested fix
- Introduce a concurrency limit via `asyncio.Semaphore` (e.g., 5–10) around `analyze_candidate`.
- Or process candidates in batches (chunks) and `await gather()` per batch.
- Keep per-candidate exception handling as-is so one failure doesn’t abort the whole consolidation.

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


View more (2)
7. Version compare blocks event loop 🐞 Bug ☼ Reliability ⭐ New
Description
compare_versions() uses synchronous subprocess.run() without a timeout, and it is called from async
sibling analysis; a slow or stuck rpmdev-vercmp call will block the event loop and stall
consolidation progress. This can manifest as long pauses during triage/rebase sibling analysis.
Code

ymir/common/version_utils.py[R35-41]

+    try:
+        result = subprocess.run(  # noqa: S603
+            ["rpmdev-vercmp", version1, version2],  # noqa: S607
+            capture_output=True,
+            text=True,
+            check=False,
+        )
Relevance

●● Moderate

Blocking subprocess in async is a concern, but switching to async subprocess/executor+timeout is a
larger behavior change.

PR-#526

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The comparator runs a blocking subprocess with no timeout, and it is invoked during async sibling
analysis when verifying that sibling target versions match.

ymir/common/version_utils.py[19-55]
ymir/agents/rebase_consolidation.py[183-186]

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

### Issue description
`compare_versions()` runs `rpmdev-vercmp` via blocking `subprocess.run()` with no timeout. Because it’s called inside async code, it blocks the event loop while executing and has no upper bound on runtime.

### Issue Context
This function is currently used by rebase sibling consolidation during triage.

### Fix Focus Areas
- ymir/common/version_utils.py[19-55]
- ymir/agents/rebase_consolidation.py[183-186]

### Suggested fix
- Add a small timeout to `subprocess.run(..., timeout=...)` and handle `subprocess.TimeoutExpired` by raising a clear `RuntimeError`.
- To avoid blocking the event loop, either:
 - run the subprocess call via `await asyncio.to_thread(compare_versions, v1, v2)` at the call site, or
 - refactor to use `asyncio.create_subprocess_exec` in an async version comparator helper.
- Keep arguments as a list (no shell) to preserve safety.

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


8. Comment fanout not deduped ✓ Resolved 🐞 Bug ☼ Reliability
Description
post_comments_to_all_issues() fans out comments to [primary] + siblings without deduplicating
issue keys, so duplicate entries in consolidated_issues will post multiple identical comments to
the same Jira issue. This can create redundant notifications and noisy issue histories.
Code

ymir/agents/rebase_agent.py[R187-202]

+        all_issues = [primary_issue] + [item.issue_key for item in consolidated_issues]
+
+        async def post_with_error_handling(issue: str) -> None:
+            try:
+                await tasks.comment_in_jira(
+                    jira_issue=issue,
+                    agent_type="Rebase",
+                    comment_text=comment_text,
+                    is_error=is_error,
+                    available_tools=available_tools,
+                    user_triggered=user_triggered,
+                )
+            except Exception as e:
+                logger.warning(f"Failed to post comment to {issue}: {e}")
+
+        await asyncio.gather(*[post_with_error_handling(issue) for issue in all_issues])
Relevance

●●● Strong

Accepted precedent: team deduped Jira comment fanout to avoid duplicate notifications/noise.

PR-#611

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new helper builds the fanout list via simple concatenation and schedules one comment per
element; no uniqueness filter is applied, so duplicates in the input will result in duplicate
comment attempts.

ymir/agents/rebase_agent.py[174-203]
PR-#611

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 parallel comment fanout constructs `all_issues` by concatenating the primary issue with sibling issue keys and then iterates it directly. If duplicates are present in `consolidated_issues`, the same Jira issue will receive multiple identical comments.

## Issue Context
Even if duplicates are not expected from the consolidation query, this function is a shared utility and should be robust against malformed/duplicated inputs.

## Fix Focus Areas
- ymir/agents/rebase_agent.py[187-202]

## Suggested fix
Deduplicate `all_issues` in an order-preserving way before calling `asyncio.gather`, e.g.:

```py
all_issues = list(dict.fromkeys([primary_issue] + [ci.issue_key for ci in consolidated_issues]))
```

Apply the same pattern anywhere else you build an issue fanout list for comments/labels if applicable.

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


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Previous review results

Review updated until commit a2b67a7 ⚖️ Balanced

Results up to commit 34fdc92 ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. JQL/test contradiction ✓ Resolved 🐞 Bug ≡ Correctness
Description
build_rebase_siblings_jql() excludes ymir_triaged_rebase, but the newly-added unit tests assert
that ymir_triaged_rebase must NOT appear in the JQL, which makes the tests fail and leaves
intended consolidation behavior ambiguous. This will block CI and/or ship incorrect sibling
filtering depending on which behavior is intended.
Code

ymir/agents/rebase_consolidation.py[R75-81]

+        excluded_labels=[
+            JiraLabels.TRIAGED_NOT_AFFECTED.value,
+            JiraLabels.TRIAGED_BACKPORT.value,
+            JiraLabels.TRIAGED_REBUILD.value,
+            JiraLabels.TRIAGED_REBASE.value,
+            JiraLabels.TRIAGED_POSTPONED.value,
+        ],
Relevance

●●● Strong

Deterministic test/implementation mismatch; similar consolidation JQL behavior was adjusted before
to stop excluding triaged labels.

PR-#554

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The implementation explicitly excludes TRIAGED_REBASE, while the new tests explicitly assert it
must not be excluded; since TRIAGED_REBASE equals ymir_triaged_rebase, these cannot both be
correct and will fail tests as written.

ymir/agents/rebase_consolidation.py[65-82]
ymir/agents/tests/unit/test_rebase_consolidation.py[4-34]
ymir/common/constants.py[137-165]

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

## Issue description
`build_rebase_siblings_jql()` currently adds `JiraLabels.TRIAGED_REBASE.value` to `excluded_labels`, which makes the produced JQL exclude issues already labeled `ymir_triaged_rebase`. However, the new unit tests assert that `"ymir_triaged_rebase"` is *not* present in the JQL.

This contradiction will deterministically fail the test suite and also indicates unclear desired behavior (should already-triaged-rebase issues be eligible for consolidation or excluded to avoid circular consolidation?).

## Issue Context
- `JiraLabels.TRIAGED_REBASE.value` is defined as `"ymir_triaged_rebase"`.
- Tests currently expect `"ymir_triaged_rebase"` to be absent from the JQL.

## Fix Focus Areas
- ymir/agents/rebase_consolidation.py[65-82]
- ymir/agents/tests/unit/test_rebase_consolidation.py[4-34]
- ymir/common/constants.py[137-165]

## What to change
Choose one and make code+tests consistent:
1) If preventing circular consolidation is the goal: keep excluding `TRIAGED_REBASE` and update the tests/docstrings to assert it *is* present in the `labels not in (...)` clause.
2) If triaged-rebase issues should still be considered siblings: remove `JiraLabels.TRIAGED_REBASE.value` from `excluded_labels` and keep the current tests.

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


2. Comment fanout can crash ✓ Resolved 🐞 Bug ☼ Reliability
Description
The rebase workflow now posts Jira comments to multiple issues via asyncio.gather() without
per-issue error handling; a single Jira API failure will raise and abort the workflow. This can
trigger retries after side effects (e.g., MR already opened) and leave some consolidated issues
without status updates.
Code

ymir/agents/rebase_agent.py[R440-454]

+                    # Post same success message to all issues in parallel
+                    all_issues = [state.jira_issue] + [item.issue_key for item in state.consolidated_issues]
+                    await asyncio.gather(
+                        *[
+                            tasks.comment_in_jira(
+                                jira_issue=issue,
+                                agent_type="Rebase",
+                                comment_text=comment_text,
+                                is_error=is_error,
+                                available_tools=gateway_tools,
+                                user_triggered=user_triggered,
+                            )
+                            for issue in all_issues
+                        ]
+                    )
Relevance

●●● Strong

Team previously added per-issue try/except for Jira comment fanout to avoid aborting on one failure.

PR-#611

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
rebase_agent now uses asyncio.gather() to fan out Jira comments to multiple issues, while
tasks.comment_in_jira does not handle exceptions and will propagate failures from
run_tool(add_jira_comment). The queue-mode runner treats uncaught exceptions as retryable
failures, so comment failures can cause retries after other steps already succeeded.

ymir/agents/rebase_agent.py[432-471]
ymir/agents/tasks.py[382-403]
ymir/agents/rebase_agent.py[615-645]

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

## Issue description
`ymir/agents/rebase_agent.py` posts comments to `[primary] + consolidated` issues using `asyncio.gather(...)` on `tasks.comment_in_jira(...)`. Because `tasks.comment_in_jira()` does not catch exceptions (it directly awaits `run_tool('add_jira_comment', ...)`), any single Jira comment failure will cause `gather()` to raise and the workflow step to abort.

This is especially risky because the comment step is late in the workflow (after rebase/build/MR creation). A transient Jira failure while commenting can therefore convert an otherwise-successful run into an exception path and trigger retries.

## Issue Context
- Success path uses `asyncio.gather` for multiple comments.
- Failure path also posts to consolidated siblings without local try/except in `post_failure_comments_to_consolidated_siblings`.
- `tasks.comment_in_jira` propagates tool exceptions.

## Fix Focus Areas
- ymir/agents/rebase_agent.py[432-472]
- ymir/agents/tasks.py[382-403]
- ymir/agents/rebase_agent.py[615-645]

## What to change
- Wrap each `tasks.comment_in_jira(...)` call so one failure doesn’t abort the entire step.
 - Option A: `results = await asyncio.gather(*coros, return_exceptions=True)` and log exceptions per issue.
 - Option B: loop with per-issue `try/except` (still can be parallelized with bounded concurrency).
- Apply the same per-issue error isolation to `post_failure_comments_to_consolidated_siblings()` so a single sibling comment failure doesn’t crash the workflow.

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


Results up to commit ec90fb4 ⚖️ Balanced


🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. Sibling failure comments skipped ✓ Resolved 🐞 Bug ≡ Correctness
Description
post_failure_comments_to_consolidated_siblings() calls `tasks.comment_in_jira(...,
is_error=True), which is suppressed when user_triggered=False`, so consolidated siblings won’t
receive failure/link comments during normal automated runs. This undermines the “link siblings on
failures” behavior (siblings only get failure context when runs are ymir_todo-triggered).
Code

ymir/agents/rebase_agent.py[R218-225]

+                await tasks.comment_in_jira(
+                    jira_issue=consolidated.issue_key,
+                    agent_type="Rebase",
+                    comment_text=f"Consolidated rebase failed. See {primary_issue} for error details.",
+                    available_tools=available_tools,
+                    is_error=True,
+                    user_triggered=user_triggered,
+                )
Relevance

●●● Strong

Deterministic logic bug: is_error comments suppressed for non-user-triggered runs; breaks intended
sibling linking.

PR-#611

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helper marks sibling link comments as is_error=True, but tasks.comment_in_jira returns early
on error comments when user_triggered is false; this means the helper’s intended side-effect does
not occur in typical automated runs.

ymir/agents/rebase_agent.py[204-229]
ymir/agents/tasks.py[382-395]
ymir/agents/rebase_agent.py[472-509]

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

## Issue description
Consolidated sibling failure-link comments are posted via `tasks.comment_in_jira` with `is_error=True`, but `tasks.comment_in_jira` intentionally skips error comments unless `user_triggered=True`. As a result, on non-user-triggered (automatic) runs, consolidated sibling issues never get the failure/link comment.

## Issue Context
This helper is intended to post an informational link comment to consolidated siblings pointing to the primary issue for details, even when we suppress noisy error notifications.

## Fix Focus Areas
- ymir/agents/rebase_agent.py[216-229]

## Suggested fix
Change the sibling link comment to be non-error (e.g., `is_error=False`) so it is always posted, while keeping detailed error comments to the primary issue gated by `is_error=True`/`user_triggered` as today. If you still need special formatting for failures, consider adding a dedicated “informational failure link” helper that bypasses the error-suppression rule.

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



Remediation recommended
2. Comment fanout not deduped ✓ Resolved 🐞 Bug ☼ Reliability
Description
post_comments_to_all_issues() fans out comments to [primary] + siblings without deduplicating
issue keys, so duplicate entries in consolidated_issues will post multiple identical comments to
the same Jira issue. This can create redundant notifications and noisy issue histories.
Code

ymir/agents/rebase_agent.py[R187-202]

+        all_issues = [primary_issue] + [item.issue_key for item in consolidated_issues]
+
+        async def post_with_error_handling(issue: str) -> None:
+            try:
+                await tasks.comment_in_jira(
+                    jira_issue=issue,
+                    agent_type="Rebase",
+                    comment_text=comment_text,
+                    is_error=is_error,
+                    available_tools=available_tools,
+                    user_triggered=user_triggered,
+                )
+            except Exception as e:
+                logger.warning(f"Failed to post comment to {issue}: {e}")
+
+        await asyncio.gather(*[post_with_error_handling(issue) for issue in all_issues])
Relevance

●●● Strong

Accepted precedent: team deduped Jira comment fanout to avoid duplicate notifications/noise.

PR-#611

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new helper builds the fanout list via simple concatenation and schedules one comment per
element; no uniqueness filter is applied, so duplicates in the input will result in duplicate
comment attempts.

ymir/agents/rebase_agent.py[174-203]
PR-#611

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 parallel comment fanout constructs `all_issues` by concatenating the primary issue with sibling issue keys and then iterates it directly. If duplicates are present in `consolidated_issues`, the same Jira issue will receive multiple identical comments.

## Issue Context
Even if duplicates are not expected from the consolidation query, this function is a shared utility and should be robust against malformed/duplicated inputs.

## Fix Focus Areas
- ymir/agents/rebase_agent.py[187-202]

## Suggested fix
Deduplicate `all_issues` in an order-preserving way before calling `asyncio.gather`, e.g.:

```py
all_issues = list(dict.fromkeys([primary_issue] + [ci.issue_key for ci in consolidated_issues]))
```

Apply the same pattern anywhere else you build an issue fanout list for comments/labels if applicable.

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


Qodo Logo

Comment thread ymir/agents/rebase_consolidation.py
Comment thread ymir/agents/rebase_agent.py Outdated
@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/agents/rebase_agent.py
Comment thread ymir/agents/rebase_agent.py Outdated
@qodo-for-packit

Copy link
Copy Markdown

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

Implement consolidation logic for rebase merge requests to group
multiple CVEs targeting the same (component, stream, version) into
a single MR, matching the existing rebuild consolidation pattern.

Changes:
- Add rebase_consolidation.py module with find_rebase_siblings()
  to discover and verify sibling issues requiring the same rebase
- Extend RebaseData model with consolidated_issues and
  consolidation_summary fields, plus all_jira_issues property
- Integrate consolidate_rebase_siblings workflow step in triage
  agent with routing logic for REBASE resolution
- Update rebase agent to handle consolidated issues: include all
  in MR description, comment on all issues, label all issues
- Add unit tests for JQL building and RebaseData properties

Impact: Reduces duplicate MRs significantly (e.g., 51 dotnet CVEs
→ ~10 grouped MRs by stream). Improves reviewer efficiency and
maintains consistency with rebuild workflow.

https://redhat.atlassian.net/browse/PACKIT-5186

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
1. Fix format_jira_links_for_mr call - pass list not unpacked args
   - Was: format_jira_links_for_mr(*all_issues) with 13 issues
   - Now: format_jira_links_for_mr(all_issues) as expected
   - Fixes: "takes 1 positional argument but 13 were given" error

2. Add REBASE to applicability check routing
   - Include Resolution.REBASE in determine_target_branch routing
   - Ensures rebase issues go through consolidation workflow

3. Label consolidated siblings to prevent duplicate work
   - Set ymir_triaged_rebase on all consolidated siblings
   - Siblings skip re-triage (existing terminal label check)
   - Only primary issue queued for rebase = single build

Result: 13 dotnet CVEs now trigger ONE rebase+build instead of 13

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
When a rebase fails, all consolidated siblings must be updated with
error labels so they can be re-triaged. Previously only the primary
issue was labeled, leaving siblings stuck with ymir_triaged_rebase.

Changes:
- On graceful failure: label all issues with ymir_rebase_failed
- On exception/retry exhaustion: label all with ymir_rebase_errored
- Both remove ymir_triaged_rebase from all issues

Result: Failed rebases no longer leave orphaned issues that can't retry

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Prevent circular consolidation where two issues each find the other
as a sibling when one has already been labeled as consolidated.

Scenario:
- RHEL-100 triages first, finds RHEL-102 as sibling
- Labels RHEL-102 with ymir_triaged_rebase
- RHEL-102 later re-triaged (via ymir_todo)
- Without this fix: RHEL-102 finds RHEL-100 as sibling
- Result: circular consolidation, duplicate MRs

Fix: Add ymir_triaged_rebase to excluded labels in JQL query

Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
When triage identifies rebase siblings for consolidation, each sibling
now gets a comment linking to the primary issue. This provides immediate
visibility into which issue will handle the actual rebase work.

The comment is posted during triage (when siblings are labeled), so
users know upfront which issue to follow for status updates.

Rebase agent already handles comments on both success and failure:
- Success: all issues get MR link
- Failure: primary gets error details, siblings get link to primary

Comments are skipped in dry-run mode.

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Removed code duplication by extracting the pattern for posting failure
comments to consolidated siblings into a reusable helper function
`post_failure_comments_to_consolidated_siblings`.

This function is now used in both failure paths:
- Graceful workflow failure (comment_in_jira step)
- Retry exhaustion (final attempt error handling)

No functional changes, just DRYer code.

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Prevent consolidation of issues that were explicitly postponed during
their own triage. If a sibling issue was already triaged and marked as
postponed, it should not be included in another issue's rebase MR.

This completes the set of excluded terminal triage labels:
- ymir_triaged_not_affected
- ymir_triaged_backport
- ymir_triaged_rebuild
- ymir_triaged_rebase
- ymir_triaged_postponed (now added)

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Triage now directly checks the current package version in dist-git before
deciding on REBASE resolution. This prevents queuing unnecessary rebase
work when the package was already rebased by another issue.

**Triage agent changes:**
- Before deciding on REBASE, check current version in spec file using
  rpmspec + rpmdev-vercmp
- If package already at or beyond target version:
  * Search for the Jira issue that performed the rebase
  * Return NOT_AFFECTED instead of REBASE
  * Provide guidance referencing existing issue and build NVR
  * Suggest adding current issue to that build's Errata

**Rebase agent instruction changes (defensive):**
- Updated to return helpful guidance instead of hard error when
  "already at version" is detected
- This is a safeguard in case triage misses the check

Example scenario this fixes:
- RHEL-192466 rebases dotnet8.0 to 8.0.129
- RHEL-211859 and 12 siblings (CVEs for same package) get triaged
- Old behavior: triage returns REBASE, all queued, rebase agent errors
- New behavior: each independently triaged as NOT_AFFECTED with link
  to RHEL-192466 and guidance to add to Errata

Note: NOT_AFFECTED issues are NOT consolidated - each gets triaged
separately with individual comments, which provides better visibility.

Assisted-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
String equality check would incorrectly exclude siblings when LLM
extracts target version in slightly different format (e.g., "1.2.3"
vs "1.2.3-rc1"). Use rpmdev-vercmp for proper RPM version comparison.

Assisted-by: Claude Sonnet 4.5 (200k context) <noreply@anthropic.com>
Sequential LLM calls for sibling analysis caused significant delays,
as each candidate required both eligibility check and version analysis.
Now use asyncio.gather to parallelize all candidate analysis.

Sequential Jira API calls (comments and labels) for consolidated issues
also created unnecessary delays. All Jira updates now run in parallel
using asyncio.gather across affected issues.

This improves overall workflow responsiveness when processing issues
with multiple consolidated siblings.

Assisted-by: Claude Sonnet 4.5 (200k context) <noreply@anthropic.com>
Issue packit#8: Label update loops duplicated 3x with identical all_issues
reconstruction. Extracted update_labels_for_all_issues() helper that
handles primary + consolidated siblings in parallel. Used in success,
failure, and error paths.

Issue packit#6: JQL building logic duplicated between build_rebase_siblings_jql
and build_rebuild_siblings_jql. Extracted build_siblings_jql() utility
accepting excluded_labels list. Both functions now delegate to shared
implementation. Uses JiraLabels enum constants instead of string literals.

Assisted-by: Claude Sonnet 4.5 (200k context) <noreply@anthropic.com>
Tests incorrectly expected ymir_triaged_rebase to NOT be in the
excluded labels list. However, commit 530fccb specifically added
this exclusion to prevent circular consolidation:

Scenario: RHEL-100 triages first and labels RHEL-102 as consolidated
sibling (ymir_triaged_rebase). If RHEL-102 is later re-triaged, it
would find RHEL-100 as a sibling, creating circular consolidation.

Updated tests to:
- Assert ymir_triaged_rebase IS in the excluded labels
- Assert ymir_triaged_postponed IS in the excluded labels
- Update docstring to clarify purpose (prevent circular consolidation)

Assisted-by: Claude Sonnet 4.5 (200k context) <noreply@anthropic.com>
Previously, asyncio.gather() on comment_in_jira() calls would abort
the entire workflow step if a single Jira comment failed. This was
risky because the comment step runs late in the workflow (after
rebase/build/MR creation), so a transient Jira failure could convert
a successful run into an exception path and trigger retries.

Changes:
- Added post_comments_to_all_issues() helper that wraps each comment
  in try/except and logs failures without propagating
- Updated success path to use the helper (replaces bare gather)
- Wrapped primary issue error comments in try/except
- Updated post_failure_comments_to_consolidated_siblings() to isolate
  per-sibling failures using gather with internal error handling

Now a single Jira comment failure logs a warning but doesn't abort
the workflow or prevent comments to other issues.

Assisted-by: Claude Sonnet 4.5 (200k context) <noreply@anthropic.com>
Previously, post_failure_comments_to_consolidated_siblings() used
is_error=True when calling comment_in_jira(). This caused the comment
to be skipped on non-user-triggered (automatic) runs, because
comment_in_jira() intentionally suppresses error comments unless
user_triggered=True to avoid spamming maintainers.

However, the sibling link comment is informational (pointing to where
error details can be found), not a noisy error notification. It should
always be posted so consolidated siblings have a reference to the
primary issue.

Changed is_error=False so these informational link comments are posted
on all runs (automatic and user-triggered). Only the detailed error
comment on the primary issue remains gated by user_triggered.

Assisted-by: Claude Sonnet 4.5 (200k context) <noreply@anthropic.com>
Previously, parallel comment/label functions built all_issues by
concatenating the primary issue with consolidated sibling keys without
deduplication. If duplicates were present in consolidated_issues (or
the primary issue appeared in the sibling list), the same Jira issue
would receive multiple identical comments/label updates.

Changes:
- update_labels_for_all_issues(): Deduplicate with dict.fromkeys()
- post_comments_to_all_issues(): Deduplicate with dict.fromkeys()
- post_failure_comments_to_consolidated_siblings(): Deduplicate by
  building a dict keyed by issue_key

This makes the functions robust against malformed/duplicated inputs
while preserving order (primary issue is always first).

Assisted-by: Claude Sonnet 4.5 (200k context) <noreply@anthropic.com>
@majamassarini

Copy link
Copy Markdown
Member Author

/agentic_review

Comment on lines +159 to +173
"""Update Jira labels for primary issue and all consolidated siblings in parallel."""
# Deduplicate in case consolidated_issues contains duplicates or the primary issue
all_issues = list(dict.fromkeys([primary_issue] + [item.issue_key for item in consolidated_issues]))
await asyncio.gather(
*[
tasks.set_jira_labels(
jira_issue=issue,
labels_to_add=labels_to_add,
labels_to_remove=labels_to_remove,
dry_run=dry_run,
user_triggered=user_triggered,
)
for issue in all_issues
]
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Label fanout aborts on error 🐞 Bug ☼ Reliability

In rebase_agent.update_labels_for_all_issues(), a single Jira label update failure will raise out of
asyncio.gather() and can interrupt the overall rebase processing while leaving some issues labeled
and others not. This affects success/failure/error paths that now label primary + consolidated
siblings together.
Agent Prompt
### Issue description
`update_labels_for_all_issues()` uses `asyncio.gather()` without per-issue exception isolation. If `tasks.set_jira_labels()` raises for one issue, the whole await raises, which can disrupt the rebase task and leave inconsistent labels across the primary/sibling issues.

### Issue Context
This helper is used from multiple rebase paths (final retry exhaustion, success, and failure), so it needs to be resilient to partial Jira outages.

### Fix Focus Areas
- ymir/agents/rebase_agent.py[151-173]
- ymir/agents/rebase_agent.py[618-626]
- ymir/agents/rebase_agent.py[703-714]
- ymir/agents/rebase_agent.py[723-731]

### Suggested fix
- Wrap each `tasks.set_jira_labels(...)` call in a small inner coroutine with `try/except` (like the comment fanout helper), log warnings per issue, and continue.
  - OR use `asyncio.gather(..., return_exceptions=True)` and iterate results to log failures.
- Ensure the helper never raises due to a single sibling label failure (unless you explicitly want to fail the whole workflow).

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

Comment on lines +183 to +185
if analysis.requires_same_rebase:
cmp_result = compare_versions(analysis.target_version, rebase_data.version)
if cmp_result == 0:

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Missing target_version breaks compare 🐞 Bug ≡ Correctness

find_rebase_siblings() calls compare_versions(analysis.target_version, ...) when
requires_same_rebase is true, but target_version is optional in the SiblingRebaseAnalysis schema. If
the LLM returns null for target_version, the comparison raises and that sibling is treated as an
analysis failure (excluded).
Agent Prompt
### Issue description
`SiblingRebaseAnalysis.target_version` is nullable, but the code assumes it’s always present when `requires_same_rebase` is true and passes it into `compare_versions()`. A null value triggers an exception and causes an avoidable false-negative (sibling excluded).

### Issue Context
The exception is caught and the candidate is excluded, so the workflow won’t crash, but consolidation quality suffers and debugging is harder.

### Fix Focus Areas
- ymir/agents/rebase_consolidation.py[85-95]
- ymir/agents/rebase_consolidation.py[183-207]

### Suggested fix
- Add an explicit guard:
  - If `analysis.requires_same_rebase` and not `analysis.target_version`, return an exclusion summary like "missing target_version" without calling `compare_versions()`.
- Alternatively, enforce a Pydantic validator: `requires_same_rebase=True` => `target_version` must be non-null (and ideally non-empty).

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

Comment on lines +214 to +215
# Analyze all candidates in parallel
results = await asyncio.gather(*[analyze_candidate(c) for c in candidates])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

3. Unbounded sibling analysis concurrency 🐞 Bug ➹ Performance

find_rebase_siblings() gathers analysis for all candidates at once; with max_results=50 this can
spawn many concurrent eligibility checks and LLM/Jira calls. This can increase latency and reduce
successful consolidation under throttling or transient Jira/model issues.
Agent Prompt
### Issue description
Sibling analysis runs with `asyncio.gather()` over all candidates, with no concurrency cap. With up to 50 siblings, this can overwhelm downstream services and increase failures/timeouts.

### Issue Context
Each candidate can trigger multiple remote operations (eligibility tool + LLM agent using Jira tools), so even moderate sibling counts can be expensive.

### Fix Focus Areas
- ymir/agents/rebase_consolidation.py[125-131]
- ymir/agents/rebase_consolidation.py[214-229]

### Suggested fix
- Introduce a concurrency limit via `asyncio.Semaphore` (e.g., 5–10) around `analyze_candidate`.
- Or process candidates in batches (chunks) and `await gather()` per batch.
- Keep per-candidate exception handling as-is so one failure doesn’t abort the whole consolidation.

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

Comment on lines +35 to +41
try:
result = subprocess.run( # noqa: S603
["rpmdev-vercmp", version1, version2], # noqa: S607
capture_output=True,
text=True,
check=False,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

4. Version compare blocks event loop 🐞 Bug ☼ Reliability

compare_versions() uses synchronous subprocess.run() without a timeout, and it is called from async
sibling analysis; a slow or stuck rpmdev-vercmp call will block the event loop and stall
consolidation progress. This can manifest as long pauses during triage/rebase sibling analysis.
Agent Prompt
### Issue description
`compare_versions()` runs `rpmdev-vercmp` via blocking `subprocess.run()` with no timeout. Because it’s called inside async code, it blocks the event loop while executing and has no upper bound on runtime.

### Issue Context
This function is currently used by rebase sibling consolidation during triage.

### Fix Focus Areas
- ymir/common/version_utils.py[19-55]
- ymir/agents/rebase_consolidation.py[183-186]

### Suggested fix
- Add a small timeout to `subprocess.run(..., timeout=...)` and handle `subprocess.TimeoutExpired` by raising a clear `RuntimeError`.
- To avoid blocking the event loop, either:
  - run the subprocess call via `await asyncio.to_thread(compare_versions, v1, v2)` at the call site, or
  - refactor to use `asyncio.create_subprocess_exec` in an async version comparator helper.
- Keep arguments as a list (no shell) to preserve safety.

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

@qodo-for-packit

Copy link
Copy Markdown

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

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant