diff --git a/plugins/jira/evals/cases/create/case-001-story-cntrlplane-basic/annotations.yaml b/plugins/jira/evals/cases/create/case-001-story-cntrlplane-basic/annotations.yaml new file mode 100644 index 000000000..cc04bf739 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-001-story-cntrlplane-basic/annotations.yaml @@ -0,0 +1,19 @@ +expected_issue_type: Story +expected_project_key: CNTRLPLANE +expected_conventions: [cntrlplane] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: customfield_10855 +expected_version_format: array +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: story +notes: > + Basic CNTRLPLANE story with --version flag. Tests version normalization + (4.22 → openshift-4.22) and array format for customfield_10855 + [{"id": "PLACEHOLDER_VERSION_ID"}]. Description should follow user story + template (As a / I want / so that) with acceptance criteria. diff --git a/plugins/jira/evals/cases/create/case-001-story-cntrlplane-basic/input.yaml b/plugins/jira/evals/cases/create/case-001-story-cntrlplane-basic/input.yaml new file mode 100644 index 000000000..071c3ba3e --- /dev/null +++ b/plugins/jira/evals/cases/create/case-001-story-cntrlplane-basic/input.yaml @@ -0,0 +1,8 @@ +type: story +project_key: CNTRLPLANE +summary: "Add user dashboard for cluster metrics" +flags: "--version 4.22" +interactive_responses: | + Who: cluster administrators + What: view real-time cluster metrics in a unified dashboard + Why: reduce time spent switching between monitoring tools diff --git a/plugins/jira/evals/cases/create/case-002-bug-ocpbugs-basic/annotations.yaml b/plugins/jira/evals/cases/create/case-002-bug-ocpbugs-basic/annotations.yaml new file mode 100644 index 000000000..3c0cc9bc0 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-002-bug-ocpbugs-basic/annotations.yaml @@ -0,0 +1,20 @@ +expected_issue_type: Bug +expected_project_key: OCPBUGS +expected_conventions: [ocpbugs] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: customfield_10855 +expected_version_format: string +expected_affects_version: [{"name": "4.21"}] +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: bug +notes: > + OCPBUGS bug with both version fields. Tests that Affects Version uses + versions field as [{"name": "4.21"}] and Target Version uses + customfield_10855 as plain string "openshift-4.21" (NOT array format). + Description should follow bug template with problem description, version, + reproducibility, steps, actual/expected results. diff --git a/plugins/jira/evals/cases/create/case-002-bug-ocpbugs-basic/input.yaml b/plugins/jira/evals/cases/create/case-002-bug-ocpbugs-basic/input.yaml new file mode 100644 index 000000000..14040f67d --- /dev/null +++ b/plugins/jira/evals/cases/create/case-002-bug-ocpbugs-basic/input.yaml @@ -0,0 +1,15 @@ +type: bug +project_key: OCPBUGS +summary: "API server returns 500 when listing pods with label selector" +flags: "" +interactive_responses: | + Affects version: 4.21 + Target version: openshift-4.21 + Reproducibility: Always + Steps to reproduce: + 1. Create a pod with label app=test + 2. Run oc get pods -l app=test + 3. Observe 500 error in response + Actual results: API server returns HTTP 500 Internal Server Error + Expected results: API server returns the list of matching pods + Additional info: Occurs on clusters with more than 100 namespaces diff --git a/plugins/jira/evals/cases/create/case-003-epic-cntrlplane/annotations.yaml b/plugins/jira/evals/cases/create/case-003-epic-cntrlplane/annotations.yaml new file mode 100644 index 000000000..99a9c4601 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-003-epic-cntrlplane/annotations.yaml @@ -0,0 +1,21 @@ +expected_issue_type: Epic +expected_project_key: CNTRLPLANE +expected_conventions: [cntrlplane] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: + customfield_10011: "Multi-cluster metrics aggregation" +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: epic +notes: > + CNTRLPLANE epic with parent linking. Tests Epic Name custom field + (customfield_10011) which must match the summary exactly. Also tests + parent linking via additional_fields.parent.key = "CNTRLPLANE-500". + Description should follow epic template with objective, scope, and + acceptance criteria. diff --git a/plugins/jira/evals/cases/create/case-003-epic-cntrlplane/input.yaml b/plugins/jira/evals/cases/create/case-003-epic-cntrlplane/input.yaml new file mode 100644 index 000000000..7d78e0a7d --- /dev/null +++ b/plugins/jira/evals/cases/create/case-003-epic-cntrlplane/input.yaml @@ -0,0 +1,12 @@ +type: epic +project_key: CNTRLPLANE +summary: "Multi-cluster metrics aggregation" +flags: "--parent CNTRLPLANE-500" +interactive_responses: | + Objective: Enable aggregation of metrics across multiple managed clusters into a unified view + Scope: Prometheus federation, custom metrics pipeline, dashboard integration + Acceptance criteria: + 1. Metrics from up to 50 managed clusters aggregated within 5 minutes + 2. Custom dashboards show cross-cluster resource utilization + 3. Alert rules can reference metrics from any managed cluster + Timeframe: 2-3 sprints diff --git a/plugins/jira/evals/cases/create/case-004-story-hypershift-aro/annotations.yaml b/plugins/jira/evals/cases/create/case-004-story-hypershift-aro/annotations.yaml new file mode 100644 index 000000000..bca283cd9 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-004-story-hypershift-aro/annotations.yaml @@ -0,0 +1,19 @@ +expected_issue_type: Story +expected_project_key: CNTRLPLANE +expected_conventions: [cntrlplane, hypershift] +expected_labels: ["ai-generated-jira", "aro-hcp"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: "HyperShift / ARO" +expected_version_field: customfield_10855 +expected_version_format: array +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: story +notes: > + CNTRLPLANE story with HyperShift ARO conventions layered. Tests that + "ARO HCP" in the summary triggers auto-detection of component + "HyperShift / ARO" and label "aro-hcp". Both cntrlplane and hypershift + conventions should be loaded. Version uses CNTRLPLANE array format. diff --git a/plugins/jira/evals/cases/create/case-004-story-hypershift-aro/input.yaml b/plugins/jira/evals/cases/create/case-004-story-hypershift-aro/input.yaml new file mode 100644 index 000000000..9d5f095ad --- /dev/null +++ b/plugins/jira/evals/cases/create/case-004-story-hypershift-aro/input.yaml @@ -0,0 +1,8 @@ +type: story +project_key: CNTRLPLANE +summary: "Enable private API server for ARO HCP clusters" +flags: "--version 4.22" +interactive_responses: | + Who: ARO HCP cluster administrators + What: configure private API server endpoints for hosted control planes on Azure + Why: meet enterprise network isolation requirements for regulated workloads diff --git a/plugins/jira/evals/cases/create/case-005-bug-ocpbugs-hypershift-rosa/annotations.yaml b/plugins/jira/evals/cases/create/case-005-bug-ocpbugs-hypershift-rosa/annotations.yaml new file mode 100644 index 000000000..8de46977d --- /dev/null +++ b/plugins/jira/evals/cases/create/case-005-bug-ocpbugs-hypershift-rosa/annotations.yaml @@ -0,0 +1,19 @@ +expected_issue_type: Bug +expected_project_key: OCPBUGS +expected_conventions: [ocpbugs, hypershift] +expected_labels: ["ai-generated-jira", "rosa-hcp"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: "HyperShift / ROSA" +expected_version_field: customfield_10855 +expected_version_format: string +expected_affects_version: [{"name": "4.21"}] +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: bug +notes: > + OCPBUGS bug with both ocpbugs and hypershift conventions layered. Tests + that "ROSA HCP" in summary triggers component "HyperShift / ROSA" and + label "rosa-hcp". Version fields use OCPBUGS format: versions as + [{"name": "4.21"}] and customfield_10855 as string "openshift-4.22". diff --git a/plugins/jira/evals/cases/create/case-005-bug-ocpbugs-hypershift-rosa/input.yaml b/plugins/jira/evals/cases/create/case-005-bug-ocpbugs-hypershift-rosa/input.yaml new file mode 100644 index 000000000..743b1ecde --- /dev/null +++ b/plugins/jira/evals/cases/create/case-005-bug-ocpbugs-hypershift-rosa/input.yaml @@ -0,0 +1,15 @@ +type: bug +project_key: OCPBUGS +summary: "ROSA HCP cluster upgrade fails with OLM operator timeout" +flags: "" +interactive_responses: | + Affects version: 4.21 + Target version: openshift-4.22 + Reproducibility: Sometimes + Steps to reproduce: + 1. Create ROSA HCP cluster on version 4.21 + 2. Initiate upgrade to 4.22 + 3. Wait for upgrade to complete + Actual results: Upgrade stalls at 60% with OLM operator timeout after 30 minutes + Expected results: Upgrade completes successfully within 45 minutes + Additional info: Affects approximately 30% of upgrade attempts on clusters with 5+ installed operators diff --git a/plugins/jira/evals/cases/create/case-006-task-cntrlplane-plain/annotations.yaml b/plugins/jira/evals/cases/create/case-006-task-cntrlplane-plain/annotations.yaml new file mode 100644 index 000000000..2c63b17c7 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-006-task-cntrlplane-plain/annotations.yaml @@ -0,0 +1,19 @@ +expected_issue_type: Task +expected_project_key: CNTRLPLANE +expected_conventions: [cntrlplane] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: task +notes: > + Plain CNTRLPLANE task with no team conventions beyond project level. + No version or component specified. Description should follow task + template with what/why and acceptance criteria. Tests that the skill + correctly identifies this as non-user-facing technical work. diff --git a/plugins/jira/evals/cases/create/case-006-task-cntrlplane-plain/input.yaml b/plugins/jira/evals/cases/create/case-006-task-cntrlplane-plain/input.yaml new file mode 100644 index 000000000..ecc375ed8 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-006-task-cntrlplane-plain/input.yaml @@ -0,0 +1,11 @@ +type: task +project_key: CNTRLPLANE +summary: "Update API documentation for cluster provisioning endpoints" +flags: "" +interactive_responses: | + What: Update the API documentation to reflect recent changes to the cluster provisioning endpoints + Why: Current documentation is outdated after the v2 API migration + Acceptance criteria: + 1. All provisioning endpoints documented with request/response examples + 2. Deprecation notices added for v1 endpoints + 3. OpenAPI spec updated and validated diff --git a/plugins/jira/evals/cases/create/case-007-feature-cntrlplane/annotations.yaml b/plugins/jira/evals/cases/create/case-007-feature-cntrlplane/annotations.yaml new file mode 100644 index 000000000..4f505fb34 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-007-feature-cntrlplane/annotations.yaml @@ -0,0 +1,19 @@ +expected_issue_type: Feature +expected_project_key: CNTRLPLANE +expected_conventions: [cntrlplane] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: feature +notes: > + CNTRLPLANE feature with strategic-level content. No version or component. + Description should follow feature template with market problem, strategic + value, and success criteria. Tests that the skill applies the feature + reference guide correctly. diff --git a/plugins/jira/evals/cases/create/case-007-feature-cntrlplane/input.yaml b/plugins/jira/evals/cases/create/case-007-feature-cntrlplane/input.yaml new file mode 100644 index 000000000..64d7dca5f --- /dev/null +++ b/plugins/jira/evals/cases/create/case-007-feature-cntrlplane/input.yaml @@ -0,0 +1,12 @@ +type: feature +project_key: CNTRLPLANE +summary: "Advanced search capabilities for cluster fleet management" +flags: "" +interactive_responses: | + Market problem: Platform engineers managing 100+ clusters cannot efficiently find and filter clusters by custom attributes, leading to operational delays + Strategic value: Reduces mean time to cluster identification from 15 minutes to under 30 seconds, directly impacting incident response time + Success criteria: + - 80% of fleet management users adopt search within 3 months + - Average search latency under 500ms for fleets of 500+ clusters + - Support for at least 20 filterable attributes including custom labels + Timeline: Spans releases 4.22 through 4.23 diff --git a/plugins/jira/evals/cases/create/case-008-feature-request-rfe/annotations.yaml b/plugins/jira/evals/cases/create/case-008-feature-request-rfe/annotations.yaml new file mode 100644 index 000000000..64fc47094 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-008-feature-request-rfe/annotations.yaml @@ -0,0 +1,19 @@ +expected_issue_type: Feature Request +expected_project_key: RFE +expected_conventions: [] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: feature-request +notes: > + RFE project feature request. Tests default project key (RFE) and + issue type name ("Feature Request"). No project-specific conventions + apply. Description should follow the 4-question feature request + workflow (title, nature, business requirements, affected components). diff --git a/plugins/jira/evals/cases/create/case-008-feature-request-rfe/input.yaml b/plugins/jira/evals/cases/create/case-008-feature-request-rfe/input.yaml new file mode 100644 index 000000000..b4c2b56f0 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-008-feature-request-rfe/input.yaml @@ -0,0 +1,9 @@ +type: feature-request +project_key: RFE +summary: "Support custom SSL certificates for ROSA HCP ingress" +flags: "" +interactive_responses: | + Title: Support custom SSL certificates for ROSA HCP ingress + Nature: Customers need to use their own CA-signed certificates for ingress controllers on ROSA HCP clusters instead of the default Let's Encrypt certificates + Business requirements: Enterprise customers in regulated industries (finance, healthcare) require custom certificates to meet compliance requirements. Currently they must use workarounds that break during cluster upgrades. + Affected packages/components: Ingress, Networking diff --git a/plugins/jira/evals/cases/create/case-009-story-gcp-hcp/annotations.yaml b/plugins/jira/evals/cases/create/case-009-story-gcp-hcp/annotations.yaml new file mode 100644 index 000000000..771a4f893 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-009-story-gcp-hcp/annotations.yaml @@ -0,0 +1,22 @@ +expected_issue_type: Story +expected_project_key: GCP +expected_conventions: [gcp-hcp] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: "hypershift-operator-gcp" +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: + customfield_10028: 2.0 +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: gcp-story +notes: > + GCP project story with GCP-HCP team conventions. Tests GCP-specific + template (user story + context + requirements + technical approach + + dependencies + acceptance criteria checklist). Should auto-estimate + Story Points (customfield_10028) using the GCP sizing guide — adding + a Prometheus metric is a 2-point task per the GCP examples. Component + provided via flag. diff --git a/plugins/jira/evals/cases/create/case-009-story-gcp-hcp/input.yaml b/plugins/jira/evals/cases/create/case-009-story-gcp-hcp/input.yaml new file mode 100644 index 000000000..a481b02a8 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-009-story-gcp-hcp/input.yaml @@ -0,0 +1,16 @@ +type: story +project_key: GCP +summary: "Add Prometheus metric for hosted control plane CPU utilization" +flags: "--component hypershift-operator-gcp" +interactive_responses: | + Who: GCP HCP platform operations team + What: expose a Prometheus metric tracking CPU utilization per hosted control plane + Why: enable proactive scaling decisions and capacity planning for the management cluster + Context: Currently CPU utilization is only visible through GCP Cloud Monitoring, requiring context switching + Requirements: Metric must be available within 30 seconds of collection, labeled by cluster ID + Technical approach: Add a custom collector to the hypershift-operator-gcp that scrapes cgroup metrics + Dependencies: Requires prometheus-operator CRD access on the management cluster + Acceptance criteria: + - Metric hcp_cpu_utilization_ratio exposed on /metrics endpoint + - Grafana dashboard updated with CPU utilization panel + - Alert rule fires when utilization exceeds 80% for 5 minutes diff --git a/plugins/jira/evals/cases/create/case-010-bug-ocpbugs-version-flag/annotations.yaml b/plugins/jira/evals/cases/create/case-010-bug-ocpbugs-version-flag/annotations.yaml new file mode 100644 index 000000000..3e797ae65 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-010-bug-ocpbugs-version-flag/annotations.yaml @@ -0,0 +1,19 @@ +expected_issue_type: Bug +expected_project_key: OCPBUGS +expected_conventions: [ocpbugs] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: customfield_10855 +expected_version_format: string +expected_affects_version: [{"name": "4.21"}] +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: bug +notes: > + OCPBUGS bug with --version flag. Tests that the version flag is applied + correctly with OCPBUGS conventions: customfield_10855 as plain string + "openshift-4.21" and versions as [{"name": "4.21"}]. No component + specified. Description should follow bug template. diff --git a/plugins/jira/evals/cases/create/case-010-bug-ocpbugs-version-flag/input.yaml b/plugins/jira/evals/cases/create/case-010-bug-ocpbugs-version-flag/input.yaml new file mode 100644 index 000000000..0682f2f04 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-010-bug-ocpbugs-version-flag/input.yaml @@ -0,0 +1,15 @@ +type: bug +project_key: OCPBUGS +summary: "NodePort service intermittently unreachable on OVN-Kubernetes clusters" +flags: "--version 4.21" +interactive_responses: | + Affects version: 4.21 + Target version: openshift-4.21 + Reproducibility: Sometimes + Steps to reproduce: + 1. Create a NodePort service on an OVN-Kubernetes cluster + 2. Access the service via node IP and allocated port + 3. Repeat access every 5 seconds for 10 minutes + Actual results: Approximately 5% of requests fail with connection refused + Expected results: All requests succeed consistently + Additional info: Issue is specific to OVN-Kubernetes; does not reproduce with OpenShift SDN diff --git a/plugins/jira/evals/cases/create/case-011-story-hypershift-rosa/annotations.yaml b/plugins/jira/evals/cases/create/case-011-story-hypershift-rosa/annotations.yaml new file mode 100644 index 000000000..2c1456a29 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-011-story-hypershift-rosa/annotations.yaml @@ -0,0 +1,18 @@ +expected_issue_type: Story +expected_project_key: CNTRLPLANE +expected_conventions: [cntrlplane, hypershift] +expected_labels: ["ai-generated-jira", "rosa-hcp"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: "HyperShift / ROSA" +expected_version_field: customfield_10855 +expected_version_format: array +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: story +notes: > + CNTRLPLANE story with ROSA HCP keywords triggering HyperShift conventions. + Tests auto-detection of "ROSA HCP" → component "HyperShift / ROSA" and + label "rosa-hcp". Version uses CNTRLPLANE array format. diff --git a/plugins/jira/evals/cases/create/case-011-story-hypershift-rosa/input.yaml b/plugins/jira/evals/cases/create/case-011-story-hypershift-rosa/input.yaml new file mode 100644 index 000000000..8f21fb9c7 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-011-story-hypershift-rosa/input.yaml @@ -0,0 +1,8 @@ +type: story +project_key: CNTRLPLANE +summary: "Implement node auto-repair for ROSA HCP worker nodes" +flags: "--version 4.22" +interactive_responses: | + Who: ROSA HCP cluster operators + What: automatically detect and replace unhealthy worker nodes in hosted clusters on AWS + Why: minimize manual intervention and reduce cluster downtime caused by node failures diff --git a/plugins/jira/evals/cases/create/case-012-epic-gcp-hcp/annotations.yaml b/plugins/jira/evals/cases/create/case-012-epic-gcp-hcp/annotations.yaml new file mode 100644 index 000000000..2ed72606c --- /dev/null +++ b/plugins/jira/evals/cases/create/case-012-epic-gcp-hcp/annotations.yaml @@ -0,0 +1,20 @@ +expected_issue_type: Epic +expected_project_key: GCP +expected_conventions: [gcp-hcp] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: + customfield_10011: "Implement CMEK support for GCP hosted control plane storage" +expected_validation_warnings: [] +expected_creation_blocked: false +description_template: gcp-epic +notes: > + GCP project epic with GCP-HCP team conventions. Tests Epic Name custom + field (customfield_10011) matching summary, and GCP epic template (use + case/context, current/desired state, scope, story breakdown, acceptance + criteria). No story points on epics. diff --git a/plugins/jira/evals/cases/create/case-012-epic-gcp-hcp/input.yaml b/plugins/jira/evals/cases/create/case-012-epic-gcp-hcp/input.yaml new file mode 100644 index 000000000..5b7de99e0 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-012-epic-gcp-hcp/input.yaml @@ -0,0 +1,25 @@ +type: epic +project_key: GCP +summary: "Implement CMEK support for GCP hosted control plane storage" +flags: "" +interactive_responses: | + Use case: Enterprise customers require Customer-Managed Encryption Keys for all data at rest in hosted control planes + Current state: All etcd and PV storage uses Google-managed encryption keys with no option for customer control + Desired state: Customers can provide their own Cloud KMS keys for encrypting etcd data and persistent volumes in hosted control planes + Scope included: + - KMS key configuration during cluster creation + - Key rotation support + - Encryption status visibility in cluster dashboard + Scope excluded: + - Bring-your-own-HSM support + - Cross-region key replication + Story breakdown: + - Configure KMS key binding for etcd encryption + - Add PV encryption with customer KMS keys + - Build key rotation automation + - Add encryption status to cluster dashboard + - Write e2e tests for CMEK lifecycle + Acceptance criteria: + - Clusters created with customer KMS keys have all data encrypted with those keys + - Key rotation completes without cluster downtime + - Encryption status visible in cluster details API diff --git a/plugins/jira/evals/cases/create/case-013-story-bad-summary/annotations.yaml b/plugins/jira/evals/cases/create/case-013-story-bad-summary/annotations.yaml new file mode 100644 index 000000000..38e6cf3e8 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-013-story-bad-summary/annotations.yaml @@ -0,0 +1,21 @@ +expected_issue_type: Story +expected_project_key: CNTRLPLANE +expected_conventions: [cntrlplane] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: ["summary_starts_with_as_a"] +expected_creation_blocked: false +description_template: story +notes: > + Tests summary validation — the summary starts with "As a" which is + an anti-pattern (user story format belongs in description, not summary). + The skill should detect this and warn in validation-results.json. + The interactive response says to use the suggested summary, so the + planned-call.json summary should be corrected to something concise like + "Enable ImageTagMirrorSet configuration in HostedCluster CRs". diff --git a/plugins/jira/evals/cases/create/case-013-story-bad-summary/input.yaml b/plugins/jira/evals/cases/create/case-013-story-bad-summary/input.yaml new file mode 100644 index 000000000..aaa5b9030 --- /dev/null +++ b/plugins/jira/evals/cases/create/case-013-story-bad-summary/input.yaml @@ -0,0 +1,9 @@ +type: story +project_key: CNTRLPLANE +summary: "As a cluster admin, I want to configure ImageTagMirrorSet in HostedCluster CRs so that I can enable tag-based image proxying" +flags: "" +interactive_responses: | + Use suggested summary: yes + Who: cluster administrators + What: configure ImageTagMirrorSet in HostedCluster custom resources + Why: enable tag-based image proxying for disconnected environments diff --git a/plugins/jira/evals/cases/create/case-014-bug-credentials-in-content/annotations.yaml b/plugins/jira/evals/cases/create/case-014-bug-credentials-in-content/annotations.yaml new file mode 100644 index 000000000..475b7295a --- /dev/null +++ b/plugins/jira/evals/cases/create/case-014-bug-credentials-in-content/annotations.yaml @@ -0,0 +1,21 @@ +expected_issue_type: Bug +expected_project_key: OCPBUGS +expected_conventions: [ocpbugs] +expected_labels: ["ai-generated-jira"] +expected_security: "Red Hat Employee" +expected_content_format: markdown +expected_component: null +expected_version_field: null +expected_version_format: null +expected_affects_version: null +expected_custom_fields: {} +expected_validation_warnings: ["credentials_detected"] +expected_creation_blocked: true +description_template: bug +notes: > + Tests security validation — the interactive responses contain what + looks like AWS access key ID (AKIAIOSFODNN7EXAMPLE) and secret key. + The skill's Phase 6 security scan should detect these credentials and + BLOCK creation. planned-call.json should be empty ({}), and + validation-results.json should have creation_blocked: true with + security_warnings describing the type of credential found. diff --git a/plugins/jira/evals/cases/create/case-014-bug-credentials-in-content/input.yaml b/plugins/jira/evals/cases/create/case-014-bug-credentials-in-content/input.yaml new file mode 100644 index 000000000..681dcc95d --- /dev/null +++ b/plugins/jira/evals/cases/create/case-014-bug-credentials-in-content/input.yaml @@ -0,0 +1,15 @@ +type: bug +project_key: OCPBUGS +summary: "OAuth token refresh fails silently on managed clusters" +flags: "" +interactive_responses: | + Affects version: 4.22 + Target version: openshift-4.22 + Reproducibility: Always + Steps to reproduce: + 1. Configure OAuth with token AKIAIOSFODNN7EXAMPLE and secret wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY + 2. Wait for token expiry (1 hour) + 3. Attempt API call with expired token + Actual results: API call hangs indefinitely, no error returned + Expected results: Token refreshed automatically or clear error returned + Additional info: Cluster kubeconfig contains the service account token diff --git a/plugins/jira/evals/eval-create.yaml b/plugins/jira/evals/eval-create.yaml new file mode 100644 index 000000000..60f555648 --- /dev/null +++ b/plugins/jira/evals/eval-create.yaml @@ -0,0 +1,458 @@ +name: jira-create-eval +description: Evaluate the jira:create skill's ability to prepare Jira issue creation calls with correct type-specific templates, project conventions, smart defaults, field formatting, and input validation +skill: jira:create + +execution: + mode: case + arguments: "{type} {project_key} \"{summary}\" {flags}" + timeout: 300 + max_budget_usd: 2.0 + +runner: + type: claude-code + plugin_dirs: + - plugins/jira + system_prompt: | + Use the jira:create skill to prepare a Jira issue. + + DRY RUN MODE: Do NOT call createJiraIssue or any Jira MCP tool. + Instead, after completing all preparation phases (load guidance, parse + arguments, apply defaults, interactive prompts, summary validation, + security validation), write the EXACT createJiraIssue call you would + have made to `planned-call.json` in the current working directory. + + The JSON must match the createJiraIssue MCP tool signature: + { + "cloudId": "redhat.atlassian.net", + "projectKey": "...", + "issueTypeName": "...", + "summary": "...", + "description": "...", + "contentFormat": "markdown", + "additional_fields": { + "labels": [...], + "security": {"name": "..."}, + "components": [...], + "customfield_10855": ..., + ...any other fields + } + } + + Also write `validation-results.json` with: + { + "summary_warnings": [...], + "security_warnings": [...], + "creation_blocked": false + } + + If security validation detects credentials, set creation_blocked to + true and leave planned-call.json empty (write {} only). + + For interactive prompts, use these pre-filled responses instead of + prompting the user: + + {interactive_responses} + + For version ID lookups that would normally require + getJiraIssueTypeMetaWithFields, use placeholder ID "PLACEHOLDER_VERSION_ID". + +models: + judge: claude-opus-4-6 + +permissions: + allow: + - "Skill" + - "Read" + - "Write" + deny: + - "Bash" + - "Agent" + - "mcp__*" + +mlflow: + experiment: jira-create-eval + +dataset: + path: plugins/jira/evals/cases/create + schema: | + Each case directory contains: + - input.yaml: YAML file with fields: + - 'type' — issue type: story, bug, epic, feature, task, or feature-request + - 'project_key' — Jira project key (e.g., CNTRLPLANE, OCPBUGS, GCP, RFE) + - 'summary' — issue title/summary text + - 'flags' — optional CLI flags (e.g., "--component HyperShift --version 4.22") + - 'interactive_responses' — pre-filled answers for the skill's interactive + prompts, formatted as the skill would expect them + - annotations.yaml: Expected outcomes for scoring: + - 'expected_issue_type': the Jira issue type name (Story, Bug, Epic, etc.) + - 'expected_project_key': the project key in the API call + - 'expected_conventions': list of convention files that should have been loaded + (e.g., [cntrlplane, hypershift]) + - 'expected_labels': list of labels that must be present (always includes + "ai-generated-jira", may include team-specific labels) + - 'expected_security': security level name (always "Red Hat Employee") + - 'expected_content_format': always "markdown" + - 'expected_component': component name if one should be set, null otherwise + - 'expected_version_field': which custom field ID is used for target version + (customfield_10855), or null if no version expected + - 'expected_version_format': "array" for CNTRLPLANE-style [{"id": "..."}] or + "string" for OCPBUGS-style "openshift-X.Y", or null + - 'expected_affects_version': for OCPBUGS bugs, the versions field value, or null + - 'expected_custom_fields': dict of additional custom fields expected + (e.g., customfield_10011 for Epic Name, customfield_10028 for Story Points) + - 'expected_validation_warnings': list of expected summary/security warnings + - 'expected_creation_blocked': boolean, true if security validation should block + - 'description_template': which template the description should follow + (story, bug, epic, feature, task, feature-request, gcp-story, gcp-epic, etc.) + - 'notes': free-text explanation of the test case + +outputs: + - path: "output" + schema: | + The skill writes two files to the workspace root: + 1. planned-call.json — the createJiraIssue arguments as a JSON object + with cloudId, projectKey, issueTypeName, summary, description, + contentFormat, and additional_fields. + 2. validation-results.json — summary_warnings (array), security_warnings + (array), and creation_blocked (boolean). + +traces: + stdout: true + stderr: true + events: false + metrics: true + +judges: + - name: output_file_exists + description: Verify the skill produced planned-call.json in the workspace. + check: | + import os + files = outputs.get("files", {}) + modified = outputs.get("modified_files", {}) + all_f = {**files, **modified} + found = [k for k in all_f if os.path.basename(k) == "planned-call.json"] + if not found: + return (False, "No planned-call.json found") + return (True, f"planned-call.json found: {found[0]}") + + - name: valid_json_structure + description: | + Verify planned-call.json is valid JSON with required top-level fields. + For blocked cases (security validation), {} is acceptable. + check: | + import json + import os + ann = outputs.get("annotations", {}) + files = outputs.get("files", {}) + modified = outputs.get("modified_files", {}) + all_f = {**files, **modified} + found = {k: v for k, v in all_f.items() + if os.path.basename(k) == "planned-call.json"} + if not found: + return (False, "No planned-call.json found") + content = list(found.values())[0] + try: + data = json.loads(content) + except Exception as e: + return (False, f"Invalid JSON: {e}") + if ann.get("expected_creation_blocked"): + return (True, "Creation blocked — empty JSON is expected") + required = ["projectKey", "issueTypeName", "summary", "contentFormat"] + missing = [f for f in required if f not in data] + if missing: + return (False, f"Missing required fields: {', '.join(missing)}") + return (True, "Valid JSON with all required fields") + + - name: universal_defaults_applied + description: | + Verify the universal defaults are present: labels include "ai-generated-jira", + security is "Red Hat Employee", contentFormat is "markdown". + check: | + import json + import os + ann = outputs.get("annotations", {}) + if ann.get("expected_creation_blocked"): + return (True, "Creation blocked — defaults check skipped") + files = outputs.get("files", {}) + modified = outputs.get("modified_files", {}) + all_f = {**files, **modified} + found = {k: v for k, v in all_f.items() + if os.path.basename(k) == "planned-call.json"} + if not found: + return (False, "No planned-call.json found") + content = list(found.values())[0] + try: + data = json.loads(content) + except Exception as e: + return (False, f"Cannot parse JSON: {e}") + errors = [] + cf = data.get("contentFormat", "") + if cf != "markdown": + errors.append(f"contentFormat is '{cf}', expected 'markdown'") + af = data.get("additional_fields", {}) + labels = af.get("labels", data.get("labels", [])) + if not isinstance(labels, list) or "ai-generated-jira" not in labels: + errors.append(f"labels missing 'ai-generated-jira': {labels}") + sec = af.get("security", data.get("security", {})) + sec_name = sec.get("name", "") if isinstance(sec, dict) else "" + if sec_name != "Red Hat Employee": + errors.append(f"security.name is '{sec_name}', expected 'Red Hat Employee'") + if errors: + return (False, "; ".join(errors)) + return (True, "Universal defaults verified: ai-generated-jira label, " + "Red Hat Employee security, markdown contentFormat") + + - name: correct_issue_type + description: Verify issueTypeName matches the expected value from annotations. + check: | + import json + import os + ann = outputs.get("annotations", {}) + if ann.get("expected_creation_blocked"): + return (True, "Creation blocked — issue type check skipped") + files = outputs.get("files", {}) + modified = outputs.get("modified_files", {}) + all_f = {**files, **modified} + found = {k: v for k, v in all_f.items() + if os.path.basename(k) == "planned-call.json"} + if not found: + return (False, "No planned-call.json found") + content = list(found.values())[0] + try: + data = json.loads(content) + except Exception as e: + return (False, f"Cannot parse JSON: {e}") + actual = data.get("issueTypeName", "") + expected = ann.get("expected_issue_type", "") + if actual.lower() == expected.lower(): + return (True, f"Issue type correct: {actual}") + return (False, f"Issue type '{actual}', expected '{expected}'") + + - name: correct_project_key + description: Verify projectKey matches the expected value from annotations. + check: | + import json + import os + ann = outputs.get("annotations", {}) + if ann.get("expected_creation_blocked"): + return (True, "Creation blocked — project key check skipped") + files = outputs.get("files", {}) + modified = outputs.get("modified_files", {}) + all_f = {**files, **modified} + found = {k: v for k, v in all_f.items() + if os.path.basename(k) == "planned-call.json"} + if not found: + return (False, "No planned-call.json found") + content = list(found.values())[0] + try: + data = json.loads(content) + except Exception as e: + return (False, f"Cannot parse JSON: {e}") + actual = data.get("projectKey", "") + expected = ann.get("expected_project_key", "") + if actual == expected: + return (True, f"Project key correct: {actual}") + return (False, f"Project key '{actual}', expected '{expected}'") + + - name: convention_fields_applied + description: | + Verify project/team-specific fields from annotations are present in the + planned call: component, version fields, custom fields, team-specific labels. + check: | + import json + import os + ann = outputs.get("annotations", {}) + if ann.get("expected_creation_blocked"): + return (True, "Creation blocked — convention fields check skipped") + files = outputs.get("files", {}) + modified = outputs.get("modified_files", {}) + all_f = {**files, **modified} + found = {k: v for k, v in all_f.items() + if os.path.basename(k) == "planned-call.json"} + if not found: + return (False, "No planned-call.json found") + content = list(found.values())[0] + try: + data = json.loads(content) + except Exception as e: + return (False, f"Cannot parse JSON: {e}") + af = data.get("additional_fields", {}) + errors = [] + details = [] + # Check component + exp_comp = ann.get("expected_component") + if exp_comp: + comps = af.get("components", data.get("components", [])) + comp_names = [] + if isinstance(comps, list): + comp_names = [c.get("name", c) if isinstance(c, dict) else str(c) + for c in comps] + if exp_comp not in comp_names: + errors.append(f"Component '{exp_comp}' not found in {comp_names}") + else: + details.append(f"component={exp_comp}") + # Check version format + exp_vf = ann.get("expected_version_format") + if exp_vf: + tv = af.get("customfield_10855", data.get("customfield_10855")) + if tv is None: + errors.append("customfield_10855 (Target Version) not set") + elif exp_vf == "array" and not isinstance(tv, list): + errors.append(f"Target Version should be array, got {type(tv).__name__}: {tv}") + elif exp_vf == "string" and not isinstance(tv, str): + errors.append(f"Target Version should be string, got {type(tv).__name__}: {tv}") + else: + details.append(f"version_format={exp_vf}") + # Check affects version (OCPBUGS bugs) + exp_av = ann.get("expected_affects_version") + if exp_av: + vers = af.get("versions", data.get("versions")) + if vers is None: + errors.append("versions (Affects Version) not set") + else: + details.append(f"affects_version set") + # Check custom fields + exp_cf = ann.get("expected_custom_fields", {}) + for field_id, expected_val in exp_cf.items(): + actual_val = af.get(field_id, data.get(field_id)) + if actual_val is None: + errors.append(f"Custom field {field_id} not set") + else: + details.append(f"{field_id} set") + # Check team-specific labels + exp_labels = ann.get("expected_labels", []) + actual_labels = af.get("labels", data.get("labels", [])) + if not isinstance(actual_labels, list): + actual_labels = [] + for lbl in exp_labels: + if lbl not in actual_labels: + errors.append(f"Label '{lbl}' missing from {actual_labels}") + if errors: + return (False, "; ".join(errors)) + if not details: + details = ["no convention-specific fields expected"] + return (True, "Convention fields verified: " + ", ".join(details)) + + - name: description_quality + description: | + LLM judge assessing whether the description follows the correct type-specific + template and includes the required sections for that issue type. + prompt: | + You are evaluating whether a Jira issue description follows the correct + type-specific template from the jira:create skill. + + The planned createJiraIssue call: + + {% for path, content in outputs.files.items() if path.endswith('planned-call.json') %} + {{ content }} + {% endfor %} + + Expected outcomes for this test case: + + {{ annotations }} + + The 'description_template' field indicates which template the description + should follow. Key template requirements: + + - **story**: User story format (As a / I want / so that), acceptance criteria + - **bug**: Problem description, version, reproducibility, steps to reproduce, + actual results, expected results + - **epic**: Objective, scope, acceptance criteria (3-6 outcomes), Epic Name field + - **feature**: Market problem, strategic value, success criteria + - **task**: What/why, acceptance criteria, technical details + - **feature-request**: Title, nature/description, business requirements + - **gcp-story**: GCP team template — user story format, context/background, + requirements, technical approach, dependencies, acceptance criteria checklist + - **gcp-epic**: GCP template — use case/context, current/desired state, scope, + story breakdown, acceptance criteria + + Evaluate on a 1-5 scale: + + Score 1: No description, or description is a single line with no structure. + Score 2: Some structure but wrong template — e.g., a bug using story format, + or missing most required sections for the correct template. + Score 3: Correct template identified and partially followed — has some required + sections but is missing important ones (e.g., story without AC, bug + without steps to reproduce). + Score 4: Correct template fully followed with all required sections present. + Content is specific to the summary/context provided. + Score 5: Excellent — correct template, all sections present with specific and + actionable content, well-formatted Markdown. + + If creation_blocked is true in annotations, evaluate only whether the skill + correctly identified the security issue — score 4 for correct detection, + 5 for detection with helpful guidance about what was found. + + - name: convention_compliance + description: | + LLM judge assessing whether the correct project and team conventions were + applied — version field formats, component selection, custom fields, labels — + matching the expected conventions listed in annotations. + prompt: | + You are evaluating whether the jira:create skill correctly applied project + and team conventions when preparing a Jira issue. + + The planned createJiraIssue call: + + {% for path, content in outputs.files.items() if path.endswith('planned-call.json') %} + {{ content }} + {% endfor %} + + Validation results: + + {% for path, content in outputs.files.items() if path.endswith('validation-results.json') %} + {{ content }} + {% endfor %} + + Expected outcomes for this test case: + + {{ annotations }} + + The 'expected_conventions' field lists which convention files should have been + loaded. Key convention requirements by file: + + - **cntrlplane**: Target Version (customfield_10855) in array format + [{"id": "VERSION_ID"}], version normalization (4.21 → openshift-4.21), + no fixVersions, components are team-specific not project-enforced + - **ocpbugs**: Bugs only, Affects Version (versions) as [{"name": "4.21"}], + Target Version (customfield_10855) as plain STRING "openshift-4.21" (NOT array), + no fixVersions + - **hypershift**: Component MUST be one of "HyperShift / ARO", "HyperShift / ROSA", + or "HyperShift" based on platform keywords; platform-specific labels + (aro-hcp, rosa-hcp) + - **gcp-hcp**: Story Points (customfield_10028) as float, GCP-specific components, + team templates for stories/epics/tasks, priority scheme + + Evaluate on a 1-5 scale: + + Score 1: Wrong conventions applied (e.g., OCPBUGS string version format used + for CNTRLPLANE, or no conventions loaded at all). + Score 2: Some conventions applied but with errors — wrong version field format, + missing required component for HyperShift, wrong custom field types. + Score 3: Correct conventions identified but incompletely applied — e.g., right + component but missing team labels, or right version field but wrong format. + Score 4: All expected conventions correctly applied — correct version field format, + correct component, correct labels, correct custom fields. + Score 5: Perfect convention compliance — all fields correct, version normalization + applied, team-specific fields present, no extraneous or incorrect fields. + + If creation_blocked is true in annotations, score 4 if the skill stopped before + creating the call, 5 if it also correctly reported what it found. + +thresholds: + output_file_exists: + min_pass_rate: 1.0 + valid_json_structure: + min_pass_rate: 1.0 + universal_defaults_applied: + min_pass_rate: 1.0 + correct_issue_type: + min_pass_rate: 1.0 + correct_project_key: + min_pass_rate: 1.0 + convention_fields_applied: + min_pass_rate: 0.85 + description_quality: + min_mean: 3.5 + convention_compliance: + min_mean: 3.5