Skip to content

Canonicalize cross-stream MR titles and changelog entries by issue family - #828

Open
nforro wants to merge 1 commit into
packit:mainfrom
nforro:consistency
Open

nforro wants to merge 1 commit into
packit:mainfrom
nforro:consistency

Conversation

@nforro

@nforro nforro commented Sep 14, 2026

Copy link
Copy Markdown
Member

Use Jira summaries for CVE commit and MR titles and changelog entries. Generate and atomically share non-CVE titles by Cloners-chain root. Version records by Jira summary changes to support invalidation.

Fixes https://redhat.atlassian.net/browse/PACKIT-5208.

@qodo-for-packit

Copy link
Copy Markdown

PR Summary by Qodo

Canonicalize cross-stream titles by issue family

🐞 Bug fix ✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Canonicalizes CVE titles from Jira summaries across rebase, backport, and rebuild workflows.
• Shares non-CVE titles atomically by package and Jira Cloners-chain root.
• Versions Redis records by summary digest and applies titles consistently to changelogs.
Diagram

graph TD
  W["Package Workflows"] --> J["Jira Details"] --> D{"CVE Family?"}
  D -->|CVE| S["Summary Title"] --> R["Atomic Redis Record"] --> O["Commit MR Changelog"]
  D -->|Non-CVE| C["Clone Root"] --> T["Title Agent"] --> R
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Deterministic local title generation
  • ➕ Avoids Redis coordination and title-agent race handling.
  • ➕ Produces titles without retained shared state.
  • ➖ Cannot guarantee identical titles when sibling workflows have different change context.
  • ➖ Would require a strict deterministic formatter that may produce less descriptive titles.
2. Store canonical titles in Jira
  • ➕ Makes the canonical value visible and editable by users.
  • ➕ Provides durable storage beyond Redis expiration.
  • ➖ Requires Jira schema or field-management changes.
  • ➖ Adds write permissions, synchronization, and stale-value ownership concerns.
  • ➖ Couples workflow execution to mutating issue metadata.

Recommendation: Keep the Redis-backed family record. It builds naturally on queue-mode infrastructure, atomically resolves concurrent workers, supports summary-driven invalidation, and avoids adding Jira write-side state. The resolver and title-agent invocation could later be centralized further to reduce duplicated workflow integration code.

Files changed (16) +588 / -81

Enhancement (11) +338 / -79
backport_agent.pyApply canonical titles throughout backport workflows +64/-17

Apply canonical titles throughout backport workflows

• Resolves canonical titles for normal and inherited backports, supplies them to the Log Agent, and forces inherited commit subjects to match. Replaces the operation-specific MR metadata cache integration.

ymir/agents/backport_agent.py

log_agent.pyAdd a dedicated non-CVE title agent +19/-0

Add a dedicated non-CVE title agent

• Introduces a constrained title-only agent and prompt accessor for generating concise, user-facing canonical titles independently of full changelog generation.

ymir/agents/log_agent.py

package_update_steps.pyTrack canonical titles in package workflow state +1/-0

Track canonical titles in package workflow state

• Adds an optional canonical title field shared by package update workflow states.

ymir/agents/package_update_steps.py

instructions.j2Require verbatim canonical titles in log output +8/-3

Require verbatim canonical titles in log output

• Directs the Log Agent to preserve canonical titles exactly in changelog descriptions and output titles, including titles exceeding the normal length limit.

ymir/agents/prompts/log/instructions.j2

prompt.j2Pass canonical titles to the Log Agent +6/-0

Pass canonical titles to the Log Agent

• Renders an explicit canonical-title section requiring exact reuse for commits, merge requests, and changelogs.

ymir/agents/prompts/log/prompt.j2

title_instructions.j2Define canonical title generation rules +7/-0

Define canonical title generation rules

• Adds instructions for concise non-CVE titles based on implemented user-facing changes, excluding Jira keys and preferring accurate source changelog wording.

ymir/agents/prompts/log/title_instructions.j2

title_prompt.j2Add the non-CVE title generation prompt +12/-0

Add the non-CVE title generation prompt

• Provides Jira summary context, implemented changes, and optional source changelog wording to the title-only agent.

ymir/agents/prompts/log/title_prompt.j2

rebase_agent.pyResolve canonical titles during rebases +35/-14

Resolve canonical titles during rebases

• Fetches and applies canonical titles before log generation in Redis queue mode. Propagates CVE identifiers through initial, environment-driven, and retry workflow paths.

ymir/agents/rebase_agent.py

rebuild_agent.pyResolve canonical titles during rebuilds +36/-1

Resolve canonical titles during rebuilds

• Integrates canonical title resolution into rebuild log generation and enforces the resolved title afterward. Carries CVE identifiers through workflow execution and retries.

ymir/agents/rebuild_agent.py

tasks.pyImplement atomic issue-family title resolution +125/-37

Implement atomic issue-family title resolution

• Replaces operation-specific MR metadata caching with summary-versioned canonical records keyed by normalized CVE sets or Jira clone roots. Fetches current Jira data, generates only non-CVE cache misses, uses atomic Redis SET NX, and expires records after 30 days.

ymir/agents/tasks.py

models.pyModel canonical title inputs and Redis records +25/-7

Model canonical title inputs and Redis records

• Adds canonical title fields and dedicated title-agent schemas. Revises cached metadata to store issue-family identity and summary digest instead of operation-specific details.

ymir/common/models.py

Refactor (1) +1 / -1
reproducer_lock.pyGeneralize clone-parent warning terminology +1/-1

Generalize clone-parent warning terminology

• Removes reproducer-lock-specific wording so clone-root resolution can be reused by canonical title handling.

ymir/common/reproducer_lock.py

Tests (2) +228 / -1
test_jinja2_templates.pyTest canonical and generated title prompts +23/-0

Test canonical and generated title prompts

• Extends local prompt schemas and verifies Jira summaries remain contextual while canonical titles are rendered as exact output requirements.

ymir/agents/tests/unit/test_jinja2_templates.py

test_tasks.pyTest canonical title identity and concurrency behavior +205/-1

Test canonical title identity and concurrency behavior

• Covers atomic CVE publication, first-writer selection for non-CVEs, required generated titles, summary invalidation, CVE normalization, clone-root grouping, and generator bypass behavior.

ymir/agents/tests/unit/test_tasks.py

Documentation (2) +21 / -0
README-agents.mdDocument cross-stream canonical title behavior +20/-0

Document cross-stream canonical title behavior

• Explains CVE and non-CVE family identification, atomic title election, summary-based invalidation, expiration, and changelog behavior. Clarifies that direct and dry-run workflows bypass canonical records.

README-agents.md

data_retention_policy.mdRecord canonical title retention policy +1/-0

Record canonical title retention policy

• Adds canonical MR titles as Redis-backed data with a configured 30-day retention period.

data_retention_policy.md

@qodo-for-packit

qodo-for-packit Bot commented Sep 14, 2026

Copy link
Copy Markdown

Code Review by Qodo

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

⚠️ 14 lower-priority findings omitted to fit the comment size limit; re-run the review or view the findings in the Qodo portal.

Grey Divider


Action required

1. Issue summaries execute package macros 🐞 Bug ⛨ Security ⭐ New
Description
ensure_canonical_changelog_title and the inherited changelog path interpolate canonical titles
into RPM specs without escaping %, while _validate_canonical_title accepts RPM macro syntax. A
crafted CVE Jira summary therefore reaches committed spec content and, for inherited backports, the
subsequent rpmbuild preparation and source-package commands.
Code

ymir/agents/tasks.py[R980-981]

+                if line.strip() and not is_jira_reference:
+                    entry.content[index] = f"- {title}"
Relevance

●●● Strong

Clear RPM macro injection risk from unescaped external titles; recent history accepts defensive
validation and changelog safety fixes.

PR-#598
PR-#477

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Canonical-title validation rejects only length and selected Unicode categories, so % and RPM macro
delimiters remain valid. Both changelog writers insert the title directly, and the inherited path
immediately invokes tools that execute rpmbuild against the modified spec.

ymir/agents/tasks.py[897-906]
ymir/agents/tasks.py[977-982]
ymir/agents/backport_agent.py[929-956]
ymir/tools/unprivileged/wicked_git.py[181-185]

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

## Issue description
Canonical titles are untrusted Jira or generated display data, but they are written directly into RPM spec changelogs. Percent-prefixed RPM macro syntax can consequently be interpreted when the spec is parsed or built.

## Fix Focus Areas
- ymir/agents/tasks.py[954-982]
- ymir/agents/backport_agent.py[929-933]

## Recommended Fix
Escape RPM macro metacharacters at every spec-changelog write boundary, including both deterministic correction and inherited changelog insertion, while preserving the original title for commit and merge-request titles. Add tests using percent-prefixed macro expressions to verify the stored spec safely represents literal text.

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


2. Older summaries win cache races ✓ Resolved 🐞 Bug ≡ Correctness
Description
resolve_canonical_mr_title rereads and returns the SET NX winner without comparing its summary
digest or update timestamp to the caller's newer Jira data. When an older worker wins an initially
empty cache while a newer-summary worker is publishing concurrently, the newer worker uses the stale
title for its changelog, commit, and merge request.
Code

ymir/agents/tasks.py[R1028-1032]

+    cached = await _get_cached_canonical_metadata(redis_conn, cache_key, jira_issue, bool(cve_ids))
+    if cached is None:
+        raise RuntimeError(f"Canonical MR title disappeared after atomic create for {jira_issue}")
+    logger.info("Reused canonical MR title for %s from %s", jira_issue, cache_key)
+    return cached[0].title
Relevance

●●● Strong

Clear concurrency correctness bug contradicting the documented timestamp guarantee; similar Redis
race handling was accepted recently.

PR-#610

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The initial publication uses SET NX, so whichever worker arrives first wins regardless of Jira
summary age. The loser then rereads and returns that record without applying the timestamp checks
used for an ordinary cache hit, contradicting the documented guarantee that an older worker cannot
preserve an obsolete title over newer summary data.

ymir/agents/tasks.py[1008-1032]
ymir/agents/tasks.py[984-990]
README-agents.md[46-48]

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

## Issue description
A worker that loses the initial `SET NX` race immediately returns the winning title, even when its own Jira summary has a newer update timestamp. This lets an older snapshot remain canonical and makes the newer worker use a stale title.

## Fix Focus Areas
- ymir/agents/tasks.py[1008-1032]
- ymir/agents/tests/unit/test_tasks.py[72-121]

## Recommended Fix
After a failed `SET NX`, compare the reread record's digest and `summary_updated` with the caller's values using the same invalidation rules as the initial cache-hit path. If the caller is newer, conditionally replace the winner with Lua and retry on another race; add a concurrent cache-miss regression test where the newer summary loses the initial election but ultimately becomes canonical.

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


3. Sibling workers publish different titles ✓ Resolved 🐞 Bug ≡ Correctness
Description
_canonical_mr_title_key includes the current issue's raw summary digest in addition to the shared
family identity, so siblings only contend on one Redis key when their summaries are byte-for-byte
identical. When cloned summaries differ or a bulk summary update is still rolling out, each worker
misses independently and publishes a separate canonical title for the same family.
Code

ymir/agents/tasks.py[R947-949]

+    identity_digest = hashlib.sha256(issue_identity.encode()).hexdigest()[:16]
+    summary_digest = hashlib.sha256(jira_summary.encode()).hexdigest()[:16]
+    return f"mr_metadata:v2:{package}:{identity_digest}:{summary_digest}"
Relevance

●●● Strong

Summary digest should version records, not isolate siblings; this contradicts the PR’s stated
family-wide sharing intent.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The family is normalized to a CVE set or clone root, but the key then adds the primary issue's
summary digest. All cache reads and SET NX publication use that summary-specific key, while rebase
and rebuild explicitly pass sibling issues expecting family-wide sharing.

ymir/agents/tasks.py[941-949]
ymir/agents/tasks.py[965-992]
ymir/agents/tasks.py[1062-1089]
ymir/agents/rebase_agent.py[487-495]
ymir/agents/rebuild_agent.py[196-204]

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

## Issue description
Canonical-title keys combine the shared issue-family identity with each worker's Jira summary digest. Siblings whose summaries differ therefore publish separate records instead of participating in one atomic title election.

## Fix Focus Areas
- ymir/agents/tasks.py[933-949]
- ymir/agents/tasks.py[1073-1090]

## Recommended Fix
Use a stable package-and-family Redis key, store the summary version in its metadata, and atomically compare/update that version when publishing. Use a monotonic Jira update version or equivalent ordering guard so an older worker cannot replace the current family record.

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


View high (4)
4. Changelog titles can diverge ✓ Resolved 🐞 Bug ≡ Correctness
Description
The workflows overwrite log_output.title with state.canonical_title only after LogAgent has
already modified the spec changelog, without validating the descriptive line it wrote. When the
agent paraphrases or otherwise fails to copy the supplied value exactly, the commit and merge
request use the canonical title while the staged changelog retains different text.
Code

ymir/agents/backport_agent.py[R1337-1338]

+            if state.canonical_title:
+                log_output.title = state.canonical_title
Relevance

●●● Strong

Directly undermines the PR’s stated changelog canonicalization goal; prior changelog correctness
fixes were accepted.

PR-#477

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
LogAgent has direct spec-editing tools and is instructed to add the changelog entry itself, while
the added workflow code only replaces the returned title afterward. The corrected result is
subsequently used for commit and merge request titles, so no code establishes the same postcondition
for the already-modified changelog.

ymir/agents/log_agent.py[59-89]
ymir/agents/prompts/log/instructions.j2[13-30]
ymir/agents/rebase_agent.py[496-510]
ymir/agents/rebase_agent.py[527-539]
ymir/agents/rebuild_agent.py[206-220]

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

## Issue description
Normal backport, rebase, and rebuild flows trust LogAgent to copy the canonical title into the spec changelog, then only correct the returned title after the agent has already edited the file. This can leave the changelog inconsistent with the commit and merge request titles.

## Fix Focus Areas
- ymir/agents/backport_agent.py[1323-1338]
- ymir/agents/rebase_agent.py[496-510]
- ymir/agents/rebuild_agent.py[206-220]

## Recommended Fix
After LogAgent finishes, deterministically inspect the newly added changelog entry and verify that its descriptive line equals `state.canonical_title` exactly. Correct the entry through a non-LLM helper or fail the workflow before staging when it differs, and reuse the same verification helper in all three workflows.

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


5. Consolidated titles omit sibling context ✓ Resolved 🐞 Bug ≡ Correctness
Description
resolve_current_canonical_mr_title receives only the primary issue and CVE identity even when the
rebase or rebuild state contains consolidated issues from other families. When such issues are
consolidated, an existing primary-family title replaces the multi-issue title, or a newly generated
multi-issue title is cached for later single-family work through the primary family's key.
Code

ymir/agents/rebase_agent.py[R490-493]

+                        package=state.package,
+                        jira_issue=state.jira_issue,
+                        cve_id=state.cve_id,
+                        generate_title=generate_title,
Relevance

●●● Strong

Recent consolidation precedents accept correctness fixes involving sibling state and cross-workflow
consistency.

PR-#726
PR-#785

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Sibling discovery groups issues by component, fix version, workflow labels, status, and comments
rather than CVE identity or Cloners ancestry. The consolidated issue model carries no family
identity, while both changed callers pass only the primary issue and CVE to the resolver and then
overwrite the generated title with the returned primary-family title.

ymir/agents/rebase_consolidation.py[45-65]
ymir/agents/rebase_consolidation.py[589-655]
ymir/agents/rebuild_consolidation.py[33-48]
ymir/common/models.py[355-367]
ymir/agents/tasks.py[844-860]
ymir/agents/tasks.py[914-973]
ymir/agents/rebase_agent.py[487-510]
ymir/agents/rebuild_agent.py[196-220]

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

## Issue description
Canonical-title resolution scopes consolidated rebase and rebuild work only to the primary Jira/CVE family, although consolidated issues are not guaranteed to belong to that family. This can apply a primary-only title to multi-family work or cache a multi-family title for later single-family workers.

## Fix Focus Areas
- ymir/agents/rebase_agent.py[487-509]
- ymir/agents/rebuild_agent.py[196-219]
- ymir/agents/tasks.py[914-973]
- ymir/common/models.py[355-367]

## Recommended Fix
Resolve the family identity for every issue included in a consolidated task. Use the canonical family record only when all included issues resolve to the same family; otherwise generate a title from the complete consolidated change and do not publish or retrieve it through the primary family's canonical cache key.

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


6. Sibling updates replace shared titles ✓ Resolved 🐞 Bug ≡ Correctness
Description
resolve_canonical_mr_title treats any different sibling summary with a newer generic Jira
updated timestamp as a newer version of the family record. When cross-stream siblings have
stream-specific summaries or an unrelated field update, a later worker replaces the elected title
even though no relevant summary changed, so streams can receive different commit, changelog, and
merge-request titles.
Code

ymir/agents/tasks.py[R1003-1005]

+        if cached_metadata.summary_updated and summary_updated <= cached_metadata.summary_updated:
+            logger.info("Kept newer canonical MR title for %s from %s", jira_issue, cache_key)
+            return cached_metadata.title
Relevance

●● Moderate

Potential shared-cache ordering bug is plausible, but no close precedent confirms rejecting
sibling-local timestamp invalidation.

PR-#726

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The Redis key is based only on package and family identity, while the digest and update timestamp
come from the currently processed sibling. Repository test data demonstrates stream-specific Jira
summary suffixes, so different summaries within one cross-stream family are legitimate; the
replacement predicate nevertheless interprets the sibling-local timestamp as ordering the shared
record.

ymir/agents/tasks.py[961-969]
ymir/agents/tasks.py[993-1005]
ymir/agents/tasks.py[1047-1061]
ymir/agents/tasks.py[1095-1102]
ymir/agents/tests/unit/test_triage_agent.py[413-426]

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

## Issue description
A family-wide canonical-title record stores a digest and generic Jira update timestamp from whichever sibling last published it. Another sibling with a naturally different summary and a later unrelated Jira update can therefore replace the elected title without an actual change to the title source.

## Fix Focus Areas
- ymir/agents/tasks.py[993-1005]
- ymir/agents/tasks.py[1015-1021]
- ymir/agents/tasks.py[1047-1061]
- ymir/common/models.py[887-893]

## Recommended Fix
Record the Jira issue that supplied the canonical record's summary and only invalidate that record when the same source issue's summary digest changes with a newer timestamp. Family siblings encountering a record from another issue must reuse it rather than treating their different summary and generic update timestamp as a replacement version; add tests covering different sibling summaries and unrelated newer updates.

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


7. Jira text changes package commits ✓ Resolved 🐞 Bug ⛨ Security
Description
resolve_canonical_mr_title assigns the Jira summary directly as a CVE title, and the log prompt
interpolates that title as instruction-bearing text without a data boundary. When an actor can
modify a CVE Jira summary, injected instructions reach LogAgent's shell and file-writing tools in
the dist-git clone, whose index is subsequently committed and pushed.
Code

ymir/agents/tasks.py[886]

+    title = jira_summary if cve_ids else generated_title
Relevance

●● Moderate

Security concern is plausible, but history lacks a closely matching precedent for prompt-injection
boundaries in this flow.

PR-#571
PR-#598

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new flow fetches the Jira summary, selects it verbatim as a CVE canonical title, and embeds it
in the LogAgent prompt. That agent has shell and write tools in the working clone, and the workflow
commits and pushes the resulting index.

ymir/agents/tasks.py[923-931]
ymir/agents/tasks.py[877-888]
ymir/agents/prompts/log/prompt.j2[5-9]
ymir/agents/log_agent.py[59-75]
ymir/tools/unprivileged/commands.py[102-125]
ymir/agents/backport_agent.py[1323-1338]
ymir/agents/tasks.py[451-496]

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

## Issue description
CVE Jira summaries now become canonical titles and are interpolated into the write-capable LogAgent prompt. A Jira summary containing model instructions can therefore influence tool use rather than being handled solely as a title value.

## Fix Focus Areas
- ymir/agents/tasks.py[874-911]
- ymir/agents/prompts/log/prompt.j2[5-9]
- ymir/agents/backport_agent.py[1323-1338]

## Recommended Fix
Validate canonical titles as bounded, single-line display data before storing or using them, and pass the value to changelog/commit construction without placing it in an instruction-bearing agent prompt. If LogAgent must receive it, serialize it in a clearly delimited data field and add an explicit instruction that its contents are untrusted data that must never be interpreted as commands or tool instructions; prefer deterministic changelog insertion for the canonical descriptive line.

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



Remediation recommended

8. Generated titles retain Jira keys ✓ Resolved 🐞 Bug ≡ Correctness
Description
_JIRA_ISSUE_KEY_RE only recognizes RHEL-<number>, so _validate_generated_title accepts titles
containing valid keys from other Jira projects such as PACKIT-5208. When the Title Agent emits
such a title, it is published as the issue-family record and reused for later changelogs, commits,
and merge requests instead of falling back to ordinary title generation.
Code

ymir/agents/tasks.py[847]

+_JIRA_ISSUE_KEY_RE = re.compile(r"\bRHEL-\d+\b", re.IGNORECASE)
Relevance

●●● Strong

Validator contradicts the documented all-project Jira-key prohibition; broadening the regex is a
direct deterministic correctness fix.

PR-#743

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The added validator uses a pattern limited to the RHEL project, while the added title-agent
instructions prohibit Jira issue keys generally and the new operational documentation says a
title-agent result containing a Jira key must fall back. The PR itself references a PACKIT Jira key,
demonstrating that non-RHEL Jira projects are in scope for this repository.

ymir/agents/tasks.py[847-918]
ymir/agents/prompts/title/instructions.j2[3-6]
README-agents.md[59-62]

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 generated-title validator rejects only RHEL Jira keys, although the title-agent contract and canonical-title documentation prohibit Jira issue keys generally. Titles containing another project key can therefore become cached canonical titles.

## Fix Focus Areas
- ymir/agents/tasks.py[847-847]
- ymir/agents/prompts/title/instructions.j2[3-6]

## Recommended Fix
Replace the RHEL-specific issue-key expression with a conservative general Jira-key matcher (project key followed by a numeric issue ID), retaining word boundaries and case-insensitivity. Add regression coverage for a non-RHEL key such as `PACKIT-5208` to ensure generated-title validation rejects it.

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


9. Unattributed titles never refresh ✓ Resolved 🐞 Bug ☼ Reliability
Description
CachedMRMetadata.summary_source_issue permits None, while resolve_canonical_mr_title
interprets every value unequal to the current issue as a sibling record and returns its title
immediately. A schema-valid cache record without source attribution therefore bypasses digest and
timestamp invalidation for every issue until its 30-day expiry.
Code

ymir/common/models.py[R894-897]

+    summary_source_issue: str | None = Field(
+        default=None,
+        description="Jira issue that supplied the canonical title",
+    )
Relevance

●●● Strong

Nullable source attribution creates a clear cache invalidation bypass; accepted history favors
defensive handling of schema-valid malformed metadata.

PR-#743
PR-#729

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new model explicitly defaults source attribution to None, and cache validation accepts any
record that passes this model. The resolver then returns immediately whenever that nullable field
differs from the current issue, placing the digest and timestamp replacement checks out of reach.

ymir/common/models.py[884-901]
ymir/agents/tasks.py[921-942]
ymir/agents/tasks.py[1032-1043]

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

## Issue description
Canonical title cache records permit a missing `summary_source_issue`, but resolution treats such records as sibling-owned and never evaluates summary invalidation. This leaves stale titles in use until cache expiry.

## Fix Focus Areas
- ymir/common/models.py[894-897]
- ymir/agents/tasks.py[1032-1043]

## Recommended Fix
Make `summary_source_issue` required for version-two canonical records, or explicitly reject and compare-delete records where it is absent before resolving a title. Add a regression test proving an unattributed record is discarded rather than returned as a sibling title.

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


10. Inherited changelogs lose Jira links ✓ Resolved 🐞 Bug ≡ Correctness
Description
evaluate_inherit_source now passes only the canonical title to AddChangelogEntryTool, removing
the Jira suffix without adding a separate reference line. When canonical resolution succeeds for an
inherited non-CVE backport, the tool writes exactly that content, so the resulting explicit
changelog no longer identifies the Jira issue.
Code

ymir/agents/backport_agent.py[919]

+                    content=[f"- {title}" if state.canonical_title else f"- {title} ({state.jira_issue})"],
Relevance

●●● Strong

Accepted precedent supports preserving Jira reference lines; this direct inherited path clearly
omits them.

PR-#477

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The inherited path constructs the changelog content as a one-element list containing only the
canonical title, while AddChangelogEntryTool forwards the supplied list directly to
spec.add_changelog_entry and does not add references automatically. The Log Agent's own rules
explicitly handle Jira references separately from canonical descriptive text, but this direct
inherited path bypasses those rules.

ymir/agents/backport_agent.py[915-920]
ymir/tools/unprivileged/specfile.py[166-184]
ymir/agents/prompts/log/instructions.j2[16-30]

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

## Issue description
Inherited backports using a canonical title remove the former Jira suffix but do not add a separate Jira reference line, leaving non-CVE changelog entries without an issue association.

## Fix Focus Areas
- ymir/agents/backport_agent.py[915-920]
- ymir/agents/prompts/log/instructions.j2[16-30]

## Recommended Fix
Keep the canonical title verbatim as the descriptive line, then apply the same historical Jira-reference convention used by the Log Agent and add a separate `Resolves:` line when required.

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


View medium (6)
11. Embedded text disables shared titles ✓ Resolved 🐞 Bug ≡ Correctness
Description
extract_cve_ids extracts CVE-shaped substrings without checking token boundaries, converting text
such as notCVE-2026-1234 into clean CVE metadata. When that metadata is attached to a consolidated
sibling, family comparison classifies it as a CVE while the boundary-aware primary-summary check
does not, so canonical title resolution is skipped for the consolidation.
Code

ymir/tools/privileged/jira.py[422]

+    cve_ids = sorted(set(CVE_ID_PATTERN.findall(summary.upper())))
Relevance

●●● Strong

Boundary-aware CVE parsing is a correctness fix; repository history consistently accepts defensive
Jira parsing and regression tests.

PR-#743
PR-#729

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The new helper applies the unbounded shared pattern and its output is stored on every confirmed
rebase sibling. Canonical family resolution then scans that already-cleaned metadata with its
boundary-aware matcher, classifies it as a CVE family, and skips canonicalization when it differs
from the primary family.

ymir/tools/privileged/jira.py[415-423]
ymir/agents/rebase_consolidation.py[645-652]
ymir/agents/tasks.py[1177-1184]
ymir/agents/tasks.py[1201-1214]

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_cve_ids` strips valid-looking CVE substrings out of larger alphanumeric tokens. The resulting clean metadata bypasses the boundary-aware family detection in `tasks.py` and can incorrectly make a consolidated sibling appear to belong to a different CVE family.

## Fix Focus Areas
- ymir/tools/privileged/jira.py[418-423]
- ymir/tools/privileged/tests/unit/test_jira.py[528-535]

## Recommended Fix
Use the same negative alphanumeric boundaries as canonical family detection when extracting CVE identifiers. Add regression coverage proving strings such as `notCVE-2026-1234` and `CVE-2026-1234suffix` do not produce CVE metadata while standalone identifiers still normalize and deduplicate correctly.

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


12. Upstream commits poison shared titles ✓ Resolved 🐞 Bug ⛨ Security
Description
title_prompt.j2 interpolates changes_summary without marking or delimiting it as untrusted,
while inherited backports populate that value directly from the source commit message. A crafted
upstream commit can instruct the title model to emit a valid but misleading title that is cached and
reused across the issue family.
Code

ymir/agents/prompts/log/title_prompt.j2[R8-9]

+Implemented changes:
+{{ changes_summary }}
Relevance

●●● Strong

Untrusted prompt input lacks delimiting despite explicit protections elsewhere; prompt-injection
fixes are aligned with team practice.

PR-#745

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The prompt protects Jira and source-changelog text but leaves changes_summary raw. Inherited
backports pass state.inherit_change.commit_message directly at
ymir/agents/backport_agent.py:897-900; generated output is only syntactically validated at
ymir/agents/tasks.py:909-918, then published and reused through ymir/agents/tasks.py:1053-1080
and ymir/agents/tasks.py:1204-1223.

ymir/agents/prompts/log/title_prompt.j2[3-14]
ymir/agents/backport_agent.py[893-905]
ymir/agents/tasks.py[1045-1080]

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 title-generation prompt treats Jira summaries and source changelogs as untrusted but interpolates `changes_summary` as ordinary prompt text. In inherited backports this value is an upstream commit message, allowing prompt instructions to influence and persist the shared canonical title.

## Fix Focus Areas
- ymir/agents/prompts/log/title_prompt.j2[8-9]
- ymir/agents/prompts/log/title_instructions.j2[6-8]

## Recommended Fix
Explicitly classify implemented-change text as untrusted evidence in the agent instructions and place `changes_summary` inside a clearly labeled data boundary, directing the model never to interpret its contents as commands or instructions.

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


13. Malformed Jira records drop siblings ✓ Resolved 🐞 Bug ☼ Reliability
Description
check_candidate passes fields.summary directly to extract_cve_ids, and Jira's explicit None
survives the .get("summary", "") fallback before the unconditional summary.upper() raises. When
a Jira comment has already confirmed the sibling but its summary is null, the candidate-wide handler
classifies the check as failed and excludes it from consolidation, causing its rebase to run
separately without the intended canonical title.
Code

ymir/agents/rebase_consolidation.py[649]

+                        cve_id=extract_cve_ids(candidate_details.get("fields", {}).get("summary", "")),
Relevance

●●● Strong

PR #743 explicitly accepted defensive handling for nullable Jira summaries causing string-processing
failures.

PR-#743

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The extraction occurs after the Jira comment has already confirmed the candidate, but
.get("summary", "") preserves an explicitly null summary and extract_cve_ids immediately calls
summary.upper(). The candidate-wide exception handler at
ymir/agents/rebase_consolidation.py:662-664 converts that metadata error into exclusion, while
past PR #743 documents the same nullable Jira-summary behavior and shows that defensive handling has
previously been accepted in this integration area.

ymir/agents/rebase_consolidation.py[645-664]
ymir/tools/privileged/jira.py[418-421]
ymir/agents/rebase_consolidation.py[620-664]
PR-#743

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

## Issue description
`check_candidate` can receive a Jira issue whose `fields.summary` is null or otherwise non-string. Passing that value to `extract_cve_ids()` raises on `.upper()`, and the candidate-level exception handler turns this metadata problem into a failed sibling check, excluding an otherwise confirmed sibling from the consolidated rebase.

## Fix Focus Areas
- ymir/agents/rebase_consolidation.py[647-650]
- ymir/tools/privileged/jira.py[418-421]

## Recommended Fix
Normalize a missing or non-string summary to an empty string before calling `extract_cve_ids`, using the equivalent of `(candidate_details.get("fields", {}).get("summary") or "")` with appropriate handling for any non-string value. Alternatively, make `extract_cve_ids` accept `str | None` or other optional/non-string input and return `None` when no valid string summary is available. Add a regression test proving that a validated sibling with `summary=None` remains included in consolidation with `cve_id=None`.

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


14. Embedded text misclassifies families ✓ Resolved 🐞 Bug ≡ Correctness
Description
_CVE_ID_RE lacks token boundaries, so text such as notCVE-2026-1234 is treated as a valid CVE
identifier. When such text appears in a non-CVE Jira summary, canonical resolution bypasses
generated-title handling, publishes the summary verbatim, and shares it under a CVE family key.
Code

ymir/agents/tasks.py[843]

+_CVE_ID_RE = re.compile(r"CVE-\d{4}-\d{4,}", re.IGNORECASE)
Relevance

●●● Strong

Unbounded CVE matching creates a deterministic family-classification bug; regex parsing fixes are
accepted precedent.

PR-#756

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The unbounded expression is applied to both supplied metadata and the Jira summary at
ymir/agents/tasks.py:862-864. Any match creates a CVE identity at ymir/agents/tasks.py:994-998,
and ymir/agents/tasks.py:1045-1051 consequently selects the Jira summary rather than a validated
generated title.

ymir/agents/tasks.py[842-864]
ymir/agents/tasks.py[986-1001]
ymir/agents/tasks.py[1045-1052]

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 canonical-family CVE matcher accepts identifiers embedded inside larger words. This sends ordinary issues through CVE-specific title and cache-key behavior.

## Fix Focus Areas
- ymir/agents/tasks.py[842-844]

## Recommended Fix
Add appropriate token boundaries around the CVE pattern so only standalone CVE identifiers are extracted, while retaining case-insensitive matching and support for comma- or semicolon-separated sets. Add tests for both valid adjacent punctuation and invalid embedded text.

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


15. Multi-CVE siblings lose canonical titles ✓ Resolved 🐞 Bug ≡ Correctness
Description
check_candidate populates each sibling's cve_id with extract_cve_id, whose single search()
discards every CVE after the first. When the primary carries the complete CVE set, family comparison
treats its sibling as a different family and falls back to independently generated titles.
Code

ymir/agents/rebase_consolidation.py[649]

+                        cve_id=extract_cve_id(candidate_details.get("fields", {}).get("summary", "")),
Relevance

●●● Strong

Recent accepted sibling-consolidation fixes and multi-CVE guidance support preserving all CVE
identifiers for family matching.

PR-#785
PR-#655

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The rebase data contract requires all CVE identifiers, but the newly added sibling assignment calls
a helper that returns only the first regex match. Rebase then passes that truncated value to
canonical resolution, where each issue's complete normalized CVE set is its family identity and
unequal sets cause canonicalization to return None.

ymir/agents/rebase_consolidation.py[136-140]
ymir/agents/rebase_consolidation.py[645-652]
ymir/tools/privileged/jira.py[415-420]
ymir/agents/tasks.py[1165-1172]
ymir/agents/tasks.py[1190-1202]

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

## Issue description
Rebase consolidation retains only the first CVE from a sibling summary, so multi-CVE siblings can be classified as a different issue family and lose canonical title sharing.

## Fix Focus Areas
- ymir/agents/rebase_consolidation.py[647-650]
- ymir/tools/privileged/jira.py[415-420]

## Recommended Fix
Add or use a helper that extracts every valid CVE identifier from the sibling summary, normalizes and deduplicates them, and stores the complete set in `ConsolidatedIssue.cve_id` using the format accepted by canonical family resolution. Add a regression test where the primary and sibling both cover multiple CVEs and verify they resolve to the same family.

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


16. Source commits control shared titles ✓ Resolved 🐞 Bug ⛨ Security
Description
title_prompt.j2 inserts source changelog text directly into the title-agent prompt, while the
title-agent instructions tell the model to prefer that wording without declaring it untrusted data.
A crafted upstream spec changelog can therefore direct a syntactically valid agent response that is
cached for the family and used in backport changelogs, commit subjects, and merge-request titles.
Code

ymir/agents/prompts/log/title_prompt.j2[R9-12]

+{% if source_changelog %}
+Source changelog wording:
+{{ source_changelog }}
+{% endif %}
Relevance

●●● Strong

Recent prompt-safety and agent-guardrail findings are accepted; untrusted changelog data can
influence shared cached titles.

PR-#757
PR-#598

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Backport processing extracts changelog lines verbatim from an upstream commit and passes them to the
newly added title agent. The resulting generated title is validated only for display syntax,
published into Redis, and then forced into the changelog and the later commit/MR title sinks.

ymir/agents/backport_agent.py[336-384]
ymir/agents/backport_agent.py[1297-1321]
ymir/agents/prompts/log/title_instructions.j2[3-7]
ymir/agents/tasks.py[899-908]
ymir/agents/tasks.py[1033-1068]
ymir/agents/backport_agent.py[1339-1345]

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

## Issue description
Untrusted source changelog lines are inserted as ordinary title-agent prompt content, allowing embedded instructions to influence the family-wide canonical title.

## Fix Focus Areas
- ymir/agents/prompts/log/title_prompt.j2[9-12]
- ymir/agents/prompts/log/title_instructions.j2[3-7]

## Recommended Fix
Wrap source changelog content in an explicitly labelled untrusted-data block and add an instruction that its contents must never be interpreted as commands or task instructions. Restrict its permitted use to wording evidence for the requested descriptive title, and add a template or agent test containing an instruction-like changelog payload.

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


Grey Divider

Context sources
⚠️ Tickets: not configured — ticket URL found in PR but could not be fetched — check ticket provider credentials
✅ Compliance rules (platform): 8 rules
Review mode: 🧠 Deep: This push introduces substantial, bug-dense canonical-title logic across multiple workflows, Redis concurrency and invalidation paths, Jira family resolution, changelog mutation, and several independently integrated agent flows.

Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread ymir/agents/rebase_agent.py
Comment thread ymir/agents/tasks.py
@nforro

nforro commented Sep 14, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 17e666c

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 6b5af8f

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

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

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

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

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

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

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

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

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/agents/tasks.py
Comment thread ymir/agents/prompts/title/prompt.j2
@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

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

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/agents/prompts/log/title_prompt.j2 Outdated
Comment thread ymir/agents/tasks.py Outdated
Comment thread ymir/agents/tasks.py Outdated
Comment thread ymir/agents/rebase_consolidation.py
@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 2587b12

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/tools/privileged/jira.py
@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 2099acf

@nforro
nforro force-pushed the consistency branch 2 times, most recently from 69079eb to 92bda2b Compare September 15, 2026 11:22
@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

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

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 92bda2b

@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

@qodo-for-packit

Copy link
Copy Markdown

Code review by qodo was updated up to the latest commit 234914e

@nforro
nforro force-pushed the consistency branch 2 times, most recently from 1849088 to f443a94 Compare September 15, 2026 12:18
@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/common/models.py Outdated
Comment thread ymir/agents/tasks.py Outdated
@qodo-for-packit

Copy link
Copy Markdown

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

…mily

Use Jira summaries for CVE commit and MR titles and changelog entries.
Generate and atomically share non-CVE titles by Cloners-chain root.
Version records by Jira summary changes to support invalidation.

Signed-off-by: Nikola Forró <nforro@redhat.com>
Assisted-by: GPT-5.6 Terra via OpenCode
Assisted-by: GPT-5.6 Sol via OpenCode
@nforro

nforro commented Sep 15, 2026

Copy link
Copy Markdown
Member Author

/agentic_review

Comment thread ymir/agents/tasks.py
Comment on lines +980 to +981
if line.strip() and not is_jira_reference:
entry.content[index] = f"- {title}"

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. Issue summaries execute package macros 🐞 Bug ⛨ Security

ensure_canonical_changelog_title and the inherited changelog path interpolate canonical titles
into RPM specs without escaping %, while _validate_canonical_title accepts RPM macro syntax. A
crafted CVE Jira summary therefore reaches committed spec content and, for inherited backports, the
subsequent rpmbuild preparation and source-package commands.
Agent Prompt
## Issue description
Canonical titles are untrusted Jira or generated display data, but they are written directly into RPM spec changelogs. Percent-prefixed RPM macro syntax can consequently be interpreted when the spec is parsed or built.

## Fix Focus Areas
- ymir/agents/tasks.py[954-982]
- ymir/agents/backport_agent.py[929-933]

## Recommended Fix
Escape RPM macro metacharacters at every spec-changelog write boundary, including both deterministic correction and inherited changelog insertion, while preserving the original title for commit and merge-request titles. Add tests using percent-prefixed macro expressions to verify the stored spec safely represents literal text.

ⓘ 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 3faf503

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