Skip to content

Enable rebuild+backport MR consolidation#689

Merged
opohorel merged 3 commits into
packit:mainfrom
opohorel:consolidation_rebuild
Jul 21, 2026
Merged

Enable rebuild+backport MR consolidation#689
opohorel merged 3 commits into
packit:mainfrom
opohorel:consolidation_rebuild

Conversation

@opohorel

Copy link
Copy Markdown
Collaborator

The consolidation agent now discovers both ymir_backport and ymir_rebuild MRs. When a package has one backport and one rebuild MR open for the same branch, the rebuild is folded into the backport via an append-only flow: the backport branch is cherry-picked as base and the rebuild ticket(s) plus CVEs are appended to the commit message without a second Release bump.

Selection priority: backport+backport pairs are still consolidated first; backport+rebuild only when fewer than 2 backport MRs exist.

The rebuild agent now submits a consolidation job after creating its MR (same mechanism as the backport agent), so the consolidation agent can pick up both MR types on the next queue poll.

CVE handling: CVE IDs are extracted from both MR descriptions and merged into the consolidated commit only if the base backport already included a CVE: line (respecting per-package maintainer rules).

Assisted-by: Claude Opus 4.6 (Cursor)

@opohorel
opohorel requested a review from TomasKorbar July 16, 2026 13:28

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces a new feature to consolidate rebuild MRs (ymir_rebuild) into backport MRs (ymir_backport) to avoid redundant Release bumps. It adds a new rebuild_append_flow that cherry-picks the backport branch and appends rebuild Jira tickets and CVEs to the commit message and changelog. The review feedback highlights two critical issues: first, if the backport branch has no new commits, the flow will dangerously amend an already merged commit on the main branch; second, if multiple Jira issues resolve to the same MR, the agent may attempt to consolidate an MR with itself due to duplicate entries in matched_mrs.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

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

Copy link
Copy Markdown

PR Summary by Qodo

Enable backport+rebuild MR consolidation with append-only commit metadata

✨ Enhancement 📝 Documentation 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Discover both ymir_backport and ymir_rebuild MRs for consolidation eligibility.
• Fold rebuild MRs into backport MRs via append-only commit message updates (no extra Release bump).
• Trigger consolidation from rebuild agent and document/cover CVE + selection rules with tests.
Diagram

graph TD
  BP["Backport Agent"] -->|"submit_merge_job"| Q[("Redis Queue")]
  RB["Rebuild Agent"] -->|"submit_merge_job"| Q
  Q -->|"poll"| CONS["MR Consolidation Agent"] -->|"list_open_mrs (backport/rebuild)"| GL{{"GitLab MRs"}}
  CONS -->|"fork/clone + apply"| DG["Dist-git Clone"] -->|"push/open consolidated MR"| GL
  CONS -->|"comment/update"| JIRA{{"Jira"}}
  subgraph Legend
    direction LR
    _svc["Agent/Service"] ~~~ _q[("Queue")] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Cherry-pick rebuild branch but auto-drop Release bump commit
  • ➕ Keeps consolidation logic uniform (always cherry-pick both branches).
  • ➕ Avoids commit amend and regex-based metadata edits.
  • ➖ Requires reliably identifying and dropping the correct rebuild Release bump/changelog commits (error-prone across packages).
  • ➖ Higher risk of conflicts and unintended content inclusion from rebuild branch.
2. Use git trailers (e.g., `Resolves:`/`CVE:`) via interpret-trailers
  • ➕ More structured metadata management than regex substitution; easier to extend.
  • ➕ Reduces risk of malformed Resolves:/CVE: lines across amend operations.
  • ➖ Requires standardizing commit message format across agents and existing repos.
  • ➖ Still needs special-casing for “only update CVE line if base already had one.”
3. Track rebuild linkage in MR description only (no commit amend)
  • ➕ Avoids rewriting commit messages and potential audit/tracing concerns.
  • ➕ Simplifies git operations (no amend).
  • ➖ Loses metadata at the commit level (downstream tooling may rely on commit message).
  • ➖ Harder to ensure rebuild tickets/CVEs are preserved if MR description is edited.

Recommendation: The chosen append-only flow is a good fit for the stated constraint (avoid a second Release bump) while preserving ticket/CVE traceability in the consolidated commit. If this expands further, consider migrating Resolves:/CVE: handling to structured git trailers to reduce regex brittleness and make metadata updates more maintainable.

Files changed (5) +550 / -66

Enhancement (2) +405 / -62
mr_consolidation_agent.pySupport rebuild MRs with append-only consolidation path and CVE propagation +351/-62

Support rebuild MRs with append-only consolidation path and CVE propagation

• Expands MR discovery to include both backport and rebuild labels, tracks MR types in state, and adds CVE extraction/collection. Introduces a 'rebuild_append_flow' that cherry-picks only the backport branch and amends the commit message/changelog to append rebuild Jira/CVE metadata without an extra Release bump; also updates consolidated MR title/description and retry routing accordingly.

ymir/agents/mr_consolidation_agent.py

rebuild_agent.pySubmit consolidation job after rebuild MR creation +54/-0

Submit consolidation job after rebuild MR creation

• Adds a workflow step to submit a consolidation job (when enabled via 'ymir.yaml') after successfully opening a rebuild MR, using the same Redis queue mechanism as the backport agent.

ymir/agents/rebuild_agent.py

Tests (1) +106 / -1
test_mr_consolidation.pyAdd unit coverage for CVE extraction, MR typing, and selection priority +106/-1

Add unit coverage for CVE extraction, MR typing, and selection priority

• Adds tests for CVE ID extraction, MR type resolution from labels, and the selection preference of backport+backport over backport+rebuild.

ymir/agents/tests/unit/test_mr_consolidation.py

Documentation (2) +39 / -3
mr_consolidation_architecture.mdDocument rebuild+backport consolidation flow and triggers +38/-3

Document rebuild+backport consolidation flow and triggers

• Extends the architecture doc to show the rebuild agent also submits consolidation jobs and describes the rebuild+backport append-only consolidation behavior, including CVE propagation rules and selection priority.

docs/mr_consolidation_architecture.md

jira_label_workflow_routing.mdAdd 'ymir_rebuild' label to consolidation discovery documentation +1/-0

Add 'ymir_rebuild' label to consolidation discovery documentation

• Documents the new 'ymir_rebuild' MR label and clarifies that the consolidation agent uses it to find rebuild MRs for backport+rebuild consolidation.

jira_label_workflow_routing.md

@opohorel
opohorel force-pushed the consolidation_rebuild branch from 7db815e to e813c60 Compare July 16, 2026 13:41
@qodo-for-packit

qodo-for-packit Bot commented Jul 16, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

Context used
✅ Compliance rules (platform): 7 rules

Grey Divider


Action required

1. Retry re-cherry-picks commits 🐞 Bug ☼ Reliability
Description
On build verification failure, the retry logic routes backport+rebuild consolidations to
rebuild_append_flow, which cherry-picks the same base commits again without resetting the repo. This
makes retries likely to fail (empty/duplicate cherry-picks) and prevents meaningful recovery.
Code

ymir/agents/mr_consolidation_agent.py[R649-660]

+            def _retry_step_for_build(state):
+                """Determine which flow to retry on build failure."""
+                has_rebuild_other = any(t == "rebuild" for t in state.mr_types.values())
+                if has_rebuild_other and len(state.mr_types) > 1:
+                    return "rebuild_append_flow"
+                if release_strategy == "per_commit":
+                    return "per_commit_flow"
+                return "run_consolidation_agent"
+
            try:
                build_result = await build_agent.run(
                    build_prompt,
Relevance

⭐⭐⭐ High

Team previously fixed retries by resetting state after build failures (PR #273); likely accept
avoiding duplicate cherry-picks.

PR-#273

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The retry selector returns rebuild_append_flow on build failure whenever a rebuild MR is involved,
but rebuild_append_flow cherry-picks a fixed commit range into the same clone and does not reset
state between attempts.

ymir/agents/mr_consolidation_agent.py[649-677]
ymir/agents/mr_consolidation_agent.py[1019-1037]

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

### Issue description
`run_build_agent` retries by re-entering `rebuild_append_flow` for backport+rebuild scenarios, but `rebuild_append_flow` is not idempotent: it cherry-picks `dist_git_branch..base_branch` into the same working tree again. On the second attempt, those commits are already applied, so `git cherry-pick` is prone to failing (empty cherry-picks / conflicts), making retries ineffective.

### Issue Context
- Retries happen within the same workflow run and reuse `state.local_clone`.
- `rebuild_append_flow` does not perform any cleanup (`reset --hard`, `clean -fdx`, `cherry-pick --abort`) before applying commits.

### Fix Focus Areas
- Before starting `rebuild_append_flow`, ensure the repo is reset to a clean base (e.g., `git cherry-pick --abort || true`, `git reset --hard <dist_git_branch>`, `git clean -fdx`).
- Alternatively, change `_retry_step_for_build` so build failures after append flow do not retry append flow (e.g., fall back to `run_consolidation_agent` or re-run `fork_and_prepare_dist_git` to get a fresh clone).

- ymir/agents/mr_consolidation_agent.py[634-677]
- ymir/agents/mr_consolidation_agent.py[998-1037]

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


2. Release bump in append 🐞 Bug ≡ Correctness
Description
When rebuild_append_flow is used and release_strategy != "per_commit" (i.e., "merged"),
run_build_agent routes to update_release, causing a second Release bump even though the base
backport already bumped Release. This violates the intended append-only behavior and can produce an
unexpected NVR.
Code

ymir/agents/mr_consolidation_agent.py[R679-681]

            if release_strategy == "per_commit":
Relevance

⭐⭐⭐ High

They explicitly moved release bump later to prevent repeated Release updates on retries/flows (PR
#261).

PR-#261

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The workflow explicitly selects rebuild_append_flow for backport+rebuild (to avoid Release bumps),
but run_build_agent still routes merged strategy into update_release, which performs another Release
update after append flow completes.

ymir/agents/mr_consolidation_agent.py[583-590]
ymir/agents/mr_consolidation_agent.py[679-682]
ymir/agents/mr_consolidation_agent.py[998-1004]

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

### Issue description
`rebuild_append_flow` is explicitly designed to avoid a second Release bump (the backport MR already bumped Release). However, after build verification, `run_build_agent` returns `update_release` for non-`per_commit` strategies, which reintroduces an extra Release bump in the append flow.

### Issue Context
- `fork_and_prepare_dist_git` routes to `rebuild_append_flow` when there is exactly one backport MR and the other MR(s) are rebuild.
- `rebuild_append_flow` cherry-picks the backport branch and skips rebuild cherry-picks specifically to avoid redundant Release bumps.
- The workflow still calls `update_release` afterward when `release_strategy == "merged"`.

### Fix Focus Areas
- Ensure append flow does not call `update_release` regardless of `release_strategy` (e.g., set a state flag like `state.skip_release_bump=True` in `rebuild_append_flow`, or return directly to `push_and_open_mr` after successful build verification).
- Keep behavior unchanged for existing non-append flows.

- ymir/agents/mr_consolidation_agent.py[520-590]
- ymir/agents/mr_consolidation_agent.py[634-682]
- ymir/agents/mr_consolidation_agent.py[998-1182]

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



Remediation recommended

3. Zstream label dropped 🐞 Bug ≡ Correctness
Description
In label-triggered consolidation (source_issues path), state.all_open_mrs is never populated, but
push_and_open_mr derives source labels from state.all_open_mrs to propagate ZSTREAM_TARGET_LABEL.
This can drop the z-stream targeting label on the consolidated MR even when the source MR had it.
Code

↗ ymir/agents/mr_consolidation_agent.py

            if not state.jira_issues_collected or not state.merge_request_url:
Relevance

⭐⭐⭐ High

Z-stream label propagation has been actively enforced/fixed recently (PR #662); dropping it would be
unacceptable.

PR-#662

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The source_issues path returns from list_open_mrs into _resolve_source_issues, which never assigns
all_open_mrs; later, push_and_open_mr computes source_labels exclusively from all_open_mrs, so label
propagation cannot work in that path.

ymir/agents/mr_consolidation_agent.py[339-347]
ymir/agents/mr_consolidation_agent.py[259-264]
ymir/agents/mr_consolidation_agent.py[1347-1356]

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

### Issue description
`push_and_open_mr` propagates `ZSTREAM_TARGET_LABEL` by scanning `state.all_open_mrs` for the selected `state.mr_urls`. In the `source_issues` (label-triggered) path, `_resolve_source_issues` sets `mr_urls/branches/titles/descriptions` but never sets `state.all_open_mrs`, so `source_labels` becomes empty and the consolidated MR may miss `ZSTREAM_TARGET_LABEL`.

### Issue Context
This affects the label-triggered path (`source_issues`) used by the Jira label workflow; auto-discovery path populates `all_open_mrs`.

### Fix Focus Areas
- In `_resolve_source_issues`, set `state.all_open_mrs = matched_mrs` (or store the MR label sets in state) so `push_and_open_mr` can propagate labels.
- Alternatively, change `push_and_open_mr` to compute `source_labels` from the resolved MR data rather than `all_open_mrs`.

- ymir/agents/mr_consolidation_agent.py[194-280]
- ymir/agents/mr_consolidation_agent.py[339-347]
- ymir/agents/mr_consolidation_agent.py[1347-1356]

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


Grey Divider

Qodo Logo

Comment on lines +649 to 660
def _retry_step_for_build(state):
"""Determine which flow to retry on build failure."""
has_rebuild_other = any(t == "rebuild" for t in state.mr_types.values())
if has_rebuild_other and len(state.mr_types) > 1:
return "rebuild_append_flow"
if release_strategy == "per_commit":
return "per_commit_flow"
return "run_consolidation_agent"

try:
build_result = await build_agent.run(
build_prompt,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Retry re-cherry-picks commits 🐞 Bug ☼ Reliability

On build verification failure, the retry logic routes backport+rebuild consolidations to
rebuild_append_flow, which cherry-picks the same base commits again without resetting the repo. This
makes retries likely to fail (empty/duplicate cherry-picks) and prevents meaningful recovery.
Agent Prompt
### Issue description
`run_build_agent` retries by re-entering `rebuild_append_flow` for backport+rebuild scenarios, but `rebuild_append_flow` is not idempotent: it cherry-picks `dist_git_branch..base_branch` into the same working tree again. On the second attempt, those commits are already applied, so `git cherry-pick` is prone to failing (empty cherry-picks / conflicts), making retries ineffective.

### Issue Context
- Retries happen within the same workflow run and reuse `state.local_clone`.
- `rebuild_append_flow` does not perform any cleanup (`reset --hard`, `clean -fdx`, `cherry-pick --abort`) before applying commits.

### Fix Focus Areas
- Before starting `rebuild_append_flow`, ensure the repo is reset to a clean base (e.g., `git cherry-pick --abort || true`, `git reset --hard <dist_git_branch>`, `git clean -fdx`).
- Alternatively, change `_retry_step_for_build` so build failures after append flow do not retry append flow (e.g., fall back to `run_consolidation_agent` or re-run `fork_and_prepare_dist_git` to get a fresh clone).

- ymir/agents/mr_consolidation_agent.py[634-677]
- ymir/agents/mr_consolidation_agent.py[998-1037]

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

return _retry_step_for_build(state)
return "handle_failure"

if release_strategy == "per_commit":

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Release bump in append 🐞 Bug ≡ Correctness

When rebuild_append_flow is used and release_strategy != "per_commit" (i.e., "merged"),
run_build_agent routes to update_release, causing a second Release bump even though the base
backport already bumped Release. This violates the intended append-only behavior and can produce an
unexpected NVR.
Agent Prompt
### Issue description
`rebuild_append_flow` is explicitly designed to avoid a second Release bump (the backport MR already bumped Release). However, after build verification, `run_build_agent` returns `update_release` for non-`per_commit` strategies, which reintroduces an extra Release bump in the append flow.

### Issue Context
- `fork_and_prepare_dist_git` routes to `rebuild_append_flow` when there is exactly one backport MR and the other MR(s) are rebuild.
- `rebuild_append_flow` cherry-picks the backport branch and skips rebuild cherry-picks specifically to avoid redundant Release bumps.
- The workflow still calls `update_release` afterward when `release_strategy == "merged"`.

### Fix Focus Areas
- Ensure append flow does not call `update_release` regardless of `release_strategy` (e.g., set a state flag like `state.skip_release_bump=True` in `rebuild_append_flow`, or return directly to `push_and_open_mr` after successful build verification).
- Keep behavior unchanged for existing non-append flows.

- ymir/agents/mr_consolidation_agent.py[520-590]
- ymir/agents/mr_consolidation_agent.py[634-682]
- ymir/agents/mr_consolidation_agent.py[998-1182]

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

@TomasKorbar
TomasKorbar force-pushed the consolidation_rebuild branch from e813c60 to faafae6 Compare July 21, 2026 12:54
opohorel and others added 3 commits July 21, 2026 16:37
The consolidation agent now discovers both ymir_backport and
ymir_rebuild MRs.  When a package has one backport and one rebuild MR
open for the same branch, the rebuild is folded into the backport via
an append-only flow: the backport branch is cherry-picked as base and
the rebuild ticket(s) plus CVEs are appended to the commit message
without a second Release bump.

Selection priority: backport+backport pairs are still consolidated
first; backport+rebuild only when fewer than 2 backport MRs exist.

The rebuild agent now submits a consolidation job after creating its MR
(same mechanism as the backport agent), so the consolidation agent can
pick up both MR types on the next queue poll.

CVE handling: CVE IDs are extracted from both MR descriptions and
merged into the consolidated commit only if the base backport already
included a CVE: line (respecting per-package maintainer rules).

Assisted-by: Claude Opus 4.6 (Cursor)
Extract the duplicated fetch-config/check-enabled/submit-to-Redis logic
from both backport_agent and rebuild_agent into a single
try_submit_consolidation_job() function in tasks.py.

Each agent keeps its own thin wrapper for precondition checks and
error handling (backport posts a Jira comment on malformed config,
rebuild just logs).

Co-authored-by: Cursor <cursoragent@cursor.com>
@TomasKorbar
TomasKorbar force-pushed the consolidation_rebuild branch from 4f258c3 to 7579fcc Compare July 21, 2026 14:37
@opohorel
opohorel merged commit 312f5b3 into packit:main Jul 21, 2026
11 checks passed
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.

2 participants