From 7ee06bec124b4215614a07187d175aa468a4be1f Mon Sep 17 00:00:00 2001 From: Maja Massarini Date: Wed, 26 Aug 2026 10:44:52 +0200 Subject: [PATCH 1/3] Fix: Clarify which Jira field is missing in CVE eligibility error The error message 'CVE has no target release specified' was ambiguous and didn't indicate which Jira field needed to be fixed. This caused confusion when investigating triage errors. Updated the message to explicitly mention 'Fix Versions field is empty' so users can immediately identify and fix the missing field. Context: Discovered during investigation of RHEL-246615, RHEL-246616, RHEL-246377, RHEL-246559, and RHEL-246383, which all failed eligibility check after component changes cleared their Fix Versions field. Assisted-by: Claude Sonnet 4.5 --- ymir/tools/privileged/jira.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ymir/tools/privileged/jira.py b/ymir/tools/privileged/jira.py index 716a76366..ebe01a29d 100644 --- a/ymir/tools/privileged/jira.py +++ b/ymir/tools/privileged/jira.py @@ -808,8 +808,8 @@ async def _run( CVEEligibilityResult( is_cve=True, eligibility=TriageEligibility.NEVER, - reason="CVE has no target release specified", - error="CVE has no target release specified", + reason="CVE has no target release specified (Fix Versions field is empty)", + error="CVE has no target release specified (Fix Versions field is empty)", ).model_dump() ) From b296d5e86ff036daa7e835fe22d19a0fa70b88db Mon Sep 17 00:00:00 2001 From: Maja Massarini Date: Wed, 26 Aug 2026 10:44:59 +0200 Subject: [PATCH 2/3] Fix: Prevent duplicate error comments for non-retriable CVE eligibility failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE eligibility errors fall into two categories: 1. Human-correctable (missing Fix Versions field) - non-retriable 2. Operational/transient (network, API failures) - retriable Previously, ALL eligibility errors triggered retry logic, causing duplicate comments for human-correctable errors that don't benefit from retries. This change distinguishes between the two: - Missing Fix Versions field → CLARIFICATION_NEEDED (non-retriable) - Posts comment exactly once - Maps to ymir_needs_attention label (data incomplete, needs human fix) - Provides clear guidance to fix and retry manually - Clone/dependency check failures → ERROR (retriable) - Triggers normal retry logic for transient failures - Maps to ymir_triage_errored label after max retries exhausted - May recover without human intervention Resolution mapping handles all label transitions - no manual label setting. This prevents duplicate comment spam for issues like RHEL-246615 where the same 'CVE has no target release specified' error was posted 3 times, while preserving automatic retry for operational failures. Assisted-by: Claude Sonnet 4.5 --- ymir/agents/triage_agent.py | 40 ++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/ymir/agents/triage_agent.py b/ymir/agents/triage_agent.py index aede715b7..994e6aafa 100644 --- a/ymir/agents/triage_agent.py +++ b/ymir/agents/triage_agent.py @@ -593,13 +593,39 @@ async def check_cve_eligibility(state): f"Issue {state.jira_issue} not eligible for triage: {state.cve_eligibility_result.reason}" ) if state.cve_eligibility_result.error: - state.triage_result = OutputSchema( - resolution=Resolution.ERROR, - data=ErrorData( - details=f"CVE eligibility check error: {state.cve_eligibility_result.error}", - jira_issue=state.jira_issue, - ), - ) + # Distinguish human-correctable errors from transient/operational failures: + # - Missing Fix Versions field → CLARIFICATION_NEEDED (non-retriable, + # maps to ymir_needs_attention) + # - Clone/dependency check failures → ERROR (retriable, + # maps to ymir_triage_errored) + error_msg = state.cve_eligibility_result.error + is_missing_fix_version = "Fix Versions field is empty" in error_msg + + if is_missing_fix_version: + # Non-retriable: requires human intervention (e.g., setting Fix Versions field) + # Use CLARIFICATION_NEEDED to skip retry loop and post comment only once + # Maps to ymir_needs_attention label (issue data incomplete, needs human attention) + state.triage_result = OutputSchema( + resolution=Resolution.CLARIFICATION_NEEDED, + data=ClarificationNeededData( + findings=f"CVE eligibility check error: {error_msg}", + additional_info_needed=( + "Please fix the issue and retry manually (e.g., via ymir_todo label)." + ), + jira_issue=state.jira_issue, + ), + ) + else: + # Retriable: operational/transient errors (network, API failures, etc.) + # Use ERROR resolution to trigger retry logic + # Maps to ymir_triage_errored label after max retries exhausted + state.triage_result = OutputSchema( + resolution=Resolution.ERROR, + data=ErrorData( + details=f"CVE eligibility check error: {error_msg}", + jira_issue=state.jira_issue, + ), + ) elif dup_key: state.triage_result = OutputSchema( resolution=Resolution.OPEN_ENDED_ANALYSIS, From 7019db81fbe4e68b37fce6bd0f002586bb75dd9e Mon Sep 17 00:00:00 2001 From: Maja Massarini Date: Wed, 26 Aug 2026 10:45:05 +0200 Subject: [PATCH 3/3] Test: Add assertion for clarified Fix Versions error message Extend test_eligibility_no_fix_version to verify that both 'reason' and 'error' fields contain the clarified 'Fix Versions field is empty' message. This prevents regression if the improved error message is accidentally reverted. Assisted-by: Claude Sonnet 4.5 --- ymir/tools/privileged/tests/unit/test_jira.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ymir/tools/privileged/tests/unit/test_jira.py b/ymir/tools/privileged/tests/unit/test_jira.py index 1b7167e45..3264cde61 100644 --- a/ymir/tools/privileged/tests/unit/test_jira.py +++ b/ymir/tools/privileged/tests/unit/test_jira.py @@ -1105,6 +1105,9 @@ async def test_eligibility_no_fix_version(): result = (await CheckCveTriageEligibilityTool().run(input={"issue_key": "RHEL-12345"})).result assert result["eligibility"] == TriageEligibility.NEVER assert result["error"] is not None + # Verify the clarified error message that specifies which Jira field is missing + assert "Fix Versions field is empty" in result["error"] + assert "Fix Versions field is empty" in result["reason"] @pytest.mark.asyncio