with one click
microshift-ci-create-bugs
// Create JIRA bugs from analyze-ci failure reports with cross-release deduplication (dry-run by default)
// Create JIRA bugs from analyze-ci failure reports with cross-release deduplication (dry-run by default)
Check OCP release schedule, verify availability, evaluate z-stream need, or check nightly build gaps
Run the full Prow CI release testing workflow — create PR, trigger jobs, check status, merge PR, download and upload artifacts
Regenerate the CI Doctor HTML report from existing data
Analyze CI for multiple MicroShift releases and produce an HTML summary
Analyze CI for LVMS periodic jobs and produce an HTML summary
Download Prow job artifacts, identify root cause of failure, and produce a structured error report
| name | microshift-ci:create-bugs |
| argument-hint | <source1>[,<source2>,...] [--create] [--auto] |
| description | Create JIRA bugs from analyze-ci failure reports with cross-release deduplication (dry-run by default) |
| user-invocable | true |
| allowed-tools | Bash, Read, Write, Glob, Grep, Agent, mcp__jira__jira_search, mcp__jira__jira_create_issue, mcp__jira__jira_get_issue, mcp__jira__jira_get_transitions, mcp__jira__jira_transition_issue, mcp__jira__jira_add_comment |
/microshift-ci:create-bugs <source> [--create] [--auto]
/microshift-ci:create-bugs <source1>,<source2>,... [--create] [--auto]
Reads individual job analysis reports produced by microshift-ci:doctor and creates JIRA bugs in USHIFT for CI test failures. Operates in dry-run mode by default - it shows what bugs would be created without actually creating them. Use --create to perform actual issue creation.
Candidates are always fuzzy-matched across sources using token-based Jaccard similarity (50% threshold) with step-name bucketing — the same root cause appearing in multiple releases becomes a single candidate and a single Jira bug referencing all affected releases.
This command does NOT re-analyze CI jobs. It consumes existing job analysis files from <WORKDIR>.
<ARGUMENTS> (required): Source identifier(s), optionally followed by flags
<sources> (required): One or more comma-separated sources. Each source is one of:
4.22, main): Looks for files matching analyze-ci-release-<release>-job-*.txtpr-6396 or pr6396): Looks for files matching analyze-ci-prs-job-*-pr<number>-*.txtrebase-release-4.22): Resolves to the corresponding rebase PR by scanning existing analyze-ci-prs-job-* files for the matching release version in their content--create (optional): Actually create JIRA issues. Without this flag, only a dry-run report is produced.--auto (optional): Replace interactive per-candidate prompts with an auto-decision policy. Without --create, labels each candidate with what would happen (dry-run). With --create, executes the auto-decisions without prompting. See Step 3 for the auto-decision policy.<WORKDIR>:
analyze-ci-release-<release>-job-*.txt (produced by /microshift-ci:doctor)analyze-ci-prs-job-*-pr<number>-*.txt (produced by /microshift-ci:doctor)--- STRUCTURED SUMMARY --- block (see below)Each job analysis file produced by /microshift-ci:prow-job must end with a machine-readable block:
--- STRUCTURED SUMMARY ---
SEVERITY: <1-5>
STACK_LAYER: <AWS Infra|External Infrastructure|build phase|deploy phase|test setup phase|Test Configuration|test|teardown>
STEP_NAME: <the CI step where the error occurred>
ERROR_SIGNATURE: <concise, unique description of the root cause error>
ROOT_CAUSE: <one-line description of WHY the failure happened — the underlying mechanism, not the surface symptom>
RAW_ERROR: <verbatim primary error message from logs — used for deterministic grouping>
INFRASTRUCTURE_FAILURE: <true|false>
JOB_URL: <full prow job URL>
JOB_NAME: <full periodic job name>
RELEASE: <X.YY>
FINISHED: <job finish date in YYYY-MM-DD format>
--- END STRUCTURED SUMMARY ---
If a job file lacks this block, it is skipped with a warning.
Compute once at the start by running date +%y%m%d and substituting into the path below. In all commands, replace <WORKDIR> with the computed path — do not use shell variables.
/tmp/microshift-ci-claude-workdir.<YYMMDD>
Actions:
Parse <ARGUMENTS> to extract source(s) and detect --create and --auto flags
Split sources on commas to get SOURCES list (e.g., ["4.22"] or ["4.20", "4.21", "4.22", "5.0", "main"])
Compute SOURCE_TAG — a short identifier used in per-run output filenames (merged candidates, results, report). Use the first source in the list (e.g., 4.22, main, rebase-release-4.22). Do NOT concatenate all sources.
Determine mode: if --create is present, set MODE=create; otherwise MODE=dry-run
Determine today's WORKDIR path by running date +%y%m%d and substituting into /tmp/microshift-ci-claude-workdir.YYMMDD. Run mkdir -p on it.
For each source in SOURCES, run the preparation script:
python3 plugins/microshift-ci/scripts/search-bugs.py <source> --workdir <WORKDIR>
Each invocation writes <WORKDIR>/analyze-ci-bug-candidates-<source>.json containing parsed and deduplicated bug candidates with pre-computed keywords, test_ids, jobs[], and analysis_text.
Error Handling:
After loading per-source candidates (Step 1), check whether bug mapping files already exist for ALL sources. These files are written by Step 2 on every run and contain the Jira search results (duplicates[], regressions[]). If they exist and cover all per-source candidates, Step 2 can be skipped entirely.
Actions:
For each source in SOURCES, check if <WORKDIR>/analyze-ci-bugs-<source>.json exists
If ALL files exist:
a. Read each file and build a lookup map: error_signature → {duplicates, regressions} (aggregate across all source files)
b. For each per-source candidate across all sources, look up its error_signature in the map
c. If ALL candidates have a match: display a notice and skip Step 2, proceed directly to Step 2a:
Using cached Jira search results from prior run.
To force fresh Jira searches, delete the bug mapping files:
rm <WORKDIR>/analyze-ci-bugs-*.json
d. If ANY candidate has no match in the cache: discard all cached data and proceed to Step 2 (full Jira search for all candidates — do not mix cached and fresh results)
If ANY source file is missing: proceed to Step 2 (full Jira search)
For each per-source bug candidate (iterate over each source's candidate list separately — do NOT merge first), run ALL of the following searches. The keywords and test_ids fields are pre-computed by the script — use them directly.
Search A — Keyword search (multiple focused queries):
Use the pre-computed keywords array from the candidate (already filtered for stop words and ranked by specificity)
Run 2-3 separate searches in parallel, each using 1-2 keywords from the array. Do NOT put all keywords into a single text ~ query — Jira requires all terms to match, so queries with 3+ keywords are fragile and miss issues that use slightly different wording.
# Example: candidate.keywords = ["invalidclienttokenid", "cloudformation", "createstack", "aws-2"]
# Search A1: most distinctive keyword
mcp__jira__jira_search(jql='... AND issuetype = Bug AND text ~ "invalidclienttokenid" ...', limit=5)
# Search A2: second keyword
mcp__jira__jira_search(jql='... AND issuetype = Bug AND text ~ "cloudformation" ...', limit=5)
Merge and deduplicate results from all A-series queries before proceeding
Search B — Test case ID search (MANDATORY when test_ids is non-empty):
Use the pre-computed test_ids array from the candidate. For EACH ID, run TWO separate searches:
# Search B1: bare number
jql: ... AND issuetype = Bug AND text ~ "68256" AND status not in (Closed, Verified) ...
# Search B2: OCP-prefixed form (OpenShift Polarion convention)
jql: ... AND issuetype = Bug AND text ~ "OCP-68256" AND status not in (Closed, Verified) ...
Why both forms are required: Jira's text indexer treats OCP-68256 as a single token, so text ~ "68256" will NOT match issues containing OCP-68256, and vice versa. Skipping either form WILL cause missed duplicates.
After all searches:
mcp__jira__jira_get_issue to show summary and statusSearch C — Regression check (closed/verified issues): After completing searches A and B, run an additional keyword search against closed/verified issues to detect potential regressions:
mcp__jira__jira_search(
jql='((project = OCPBUGS AND component = MicroShift) OR project = USHIFT) AND issuetype = Bug AND text ~ "<keywords>" AND status in (Closed, Verified) ORDER BY updated DESC',
limit=5
)
If results are found, fetch their details with mcp__jira__jira_get_issue and flag them as "Potential regression of closed bug" — distinct from open duplicates. These should be shown to the user but do NOT block creation; they serve as a warning that a previously fixed issue may have resurfaced.
Note: Run searches in parallel where possible.
Query for open AI-generated bugs: After completing all per-candidate searches, run one additional query to fetch all open bugs with the microshift-ci-ai-generated label:
mcp__jira__jira_search(
jql="project = USHIFT AND issuetype = Bug AND labels = microshift-ci-ai-generated AND status not in (Closed, Verified) ORDER BY updated DESC",
fields="summary,status,priority,assignee,created,updated",
limit=50
)
If more than 50 results, paginate with start_at until all issues are fetched. For each issue, extract: key, summary, status name, priority name, assignee display name, created and updated truncated to date only (first 10 characters).
After completing all Jira searches, write machine-readable bug mapping files per source. For each source in SOURCES, write <WORKDIR>/analyze-ci-bugs-<source>.json using this JSON format:
{
"source": "<source>",
"date": "YYYY-MM-DD",
"candidates": [
{
"error_signature": "<error_signature>",
"severity": <N>,
"failure_type": "<build|test|infrastructure>",
"step_name": "<step_name>",
"affected_jobs": <count for this source>,
"duplicates": [
{"key": "<JIRA-KEY>", "summary": "<summary>", "status": "<status>", "assignee": "<display_name>", "updated": "<YYYY-MM-DD>"}
],
"regressions": [
{"key": "<JIRA-KEY>", "summary": "<summary>", "status": "<status>", "assignee": "<display_name>", "updated": "<YYYY-MM-DD>"}
]
}
],
"open_bugs": [
{
"key": "USHIFT-1234",
"summary": "...",
"status": "In Progress",
"priority": "Normal",
"assignee": "jdoe",
"created": "2026-05-01",
"updated": "2026-05-09"
}
]
}
create-report.py to show linked bugs in the HTML report, and are consumed by the merge step (Step 2a) for Jira-based deduplication.[] for duplicates and regressions when none are found.failure_type field must be set from the candidate's computed failure_type (via classify_breakdown). This field is required for downstream --merge to correctly skip infrastructure failures without needing stack_layer.Run the merge script (even for a single source — it produces a unified output with Jira data injected from the bug mapping files written in Step 2).
Before invoking, also check for any analyze-ci-bug-candidates-rebase-*.json files in <WORKDIR>. If found, include them in the merge so rebase PR failures are deduplicated against release failures.
python3 plugins/microshift-ci/scripts/search-bugs.py --merge <WORKDIR>/analyze-ci-bug-candidates-<source1>.json [<source2>.json ...] [<WORKDIR>/analyze-ci-bug-candidates-rebase-*.json] --output <WORKDIR>/analyze-ci-bug-candidates-merged-<SOURCE_TAG>.json --workdir <WORKDIR>
This writes <WORKDIR>/analyze-ci-bug-candidates-merged-<SOURCE_TAG>.json. Read and use this file for all subsequent steps.
Actions:
In dry-run mode (--create NOT specified):
In interactive create mode (--create specified, --auto NOT specified):
For each candidate, prompt the user:
Bug Candidate N/M:
Summary: "<derived summary>"
Severity: X (affects Y jobs total)
Step: <step_name>
Releases: <source1> (N jobs), <source2> (N jobs)
Grouped with: ← only when merged_signatures is non-empty
- <other_error_signature_1>
- <other_error_signature_2>
Jobs:
- <job_url_1>
- <job_url_2>
Potential Duplicates (open):
- USHIFT-XXXXX: "<summary>" [Status] (or OCPBUGS-YYYYY)
(or "None found")
Potential Regressions (closed):
- USHIFT-YYYYY (or OCPBUGS-YYYYY): "<summary>" [Status] potential regression
(or "None found")
Select the prompt template based on whether closed regressions were found:
# ACTION_PROMPT_WITH_REOPEN (use when closed regressions exist):
Action? [c]reate / [s]kip / [l]ink-to-existing <JIRA-KEY> / [r]eopen <JIRA-KEY>:
# ACTION_PROMPT_NO_REOPEN (use when no closed regressions exist):
Action? [c]reate / [s]kip / [l]ink-to-existing <JIRA-KEY>:
create: Proceed to Step 4
skip: Skip this candidate, move to next
link-to-existing: Validate the key by calling mcp__jira__jira_get_issue(issue_key=<JIRA-KEY>). If the issue exists, record the key and move to next. If the call fails or returns not-found, show an error (e.g., "JIRA key <JIRA-KEY> not found — check for typos") and re-prompt with the same Action? choices.
reopen: Validate the provided JIRA key before proceeding. Call mcp__jira__jira_get_issue(issue_key=<JIRA-KEY>) to confirm the issue exists, then verify that the key matches one of the candidate's closed regressions found in Search C, that the issue status is Closed or Verified, and that the issue type is Bug. If validation fails (key not found, not in the candidate's closed regression list, not in Closed/Verified state, or not a Bug), show an error (e.g., "JIRA key <JIRA-KEY> not eligible for reopen — must be a Bug closed regression") and re-prompt with the same Action? choices. If validation passes, proceed to Step 4a.
In auto dry-run mode (--auto without --create):
In auto-create mode (--auto --create):
When --auto is active, apply these rules in order for each candidate:
| Condition | Decision | Reason |
|---|---|---|
failure_type is "infrastructure" | Skip | "Infrastructure failure — not a product bug" |
| Has open duplicates from Jira search | Skip | "Duplicate of <JIRA-KEY>" |
Has closed regressions but no open duplicates — and all job finished dates are on or before the regression's updated date | Skip | "Stale failure predating fix for <JIRA-KEY> (updated <YYYY-MM-DD>)" |
Has closed regressions but no open duplicates — and any job finished date is after the regression's updated date | Create | Add "Potential regression of <JIRA-KEY>" to the bug description's Additional Info section |
| No duplicates, no regressions | Create | "No existing duplicates" |
As you process each candidate (applying auto-decision policy or prompting user), build a results array. After all candidates are processed (and Step 4/4a completes for create mode), write the results to <WORKDIR>/analyze-ci-bug-results-<SOURCE_TAG>.json:
{
"mode": "dry-run",
"date": "YYYY-MM-DD",
"results": [
{
"error_signature": "<matches candidate's error_signature exactly>",
"action": "create",
"jira_key": "USHIFT-1234",
"skip_category": "",
"reason": "No existing duplicates"
},
{
"error_signature": "<matches candidate's error_signature exactly>",
"action": "skip",
"jira_key": "",
"skip_category": "duplicate",
"reason": "Duplicate of USHIFT-6938"
}
]
}
All fields are required on every entry:
error_signature: must match the candidate's error_signature exactlyaction: one of create, skip, link, reopen, failedjira_key: the JIRA key for create/link/reopen; empty string "" for skip/failedskip_category: one of duplicate, infrastructure, stale_regression for skip; empty string "" for other actionsreason: human-readable explanation, always non-emptySet mode to "dry-run" or "create" matching the current run mode. Set date to today's date (YYYY-MM-DD).
Actions: For each candidate where user chose "create":
Construct the bug summary:
"MicroShift CI: <error_signature>" (truncate to 100 chars if needed)Construct the bug description using Markdown format (the MCP Jira tool accepts Markdown and automatically converts it to Jira wiki markup — do NOT write Jira wiki markup directly):
## Description of problem
CI job failures detected across MicroShift releases: <release1>, <release2>, ...
<concise description derived from the error signature and analysis text>
## Version-Release number of selected component (if applicable)
<release1>, <release2>, ...
## How reproducible
Always (fails consistently in CI across multiple releases)
## Steps to Reproduce
1. Run the CI job(s) listed below
2. Observe failure in step: <step_name>
## Actual results
````
<error details extracted from the analysis text — the specific error message and relevant log context>
````
## Expected results
CI job should pass successfully.
## Additional info
**Stack Layer:** <stack_layer>
**CI Step:** <step_name>
**Error Severity:** <severity>/5
**Number of affected jobs:** <total count across all releases>
**Last observed:** <latest finished date across all jobs, YYYY-MM-DD>
**Affected Releases:** <release1> (<N> jobs), <release2> (<N> jobs)
**Affected Jobs:**
<for each release>
*<release>:*
<for each job in release>
- [<job_name>](<job_url>)
</for each>
</for each>
**Source:** Auto-generated by /microshift-ci:create-bugs from CI analysis output.
Create the issue:
mcp__jira__jira_create_issue(
project_key="USHIFT",
summary="MicroShift CI: <error_signature>",
issue_type="Bug",
description="<constructed description>",
components="MicroShift",
additional_fields={
"labels": ["microshift-ci-ai-generated"]
}
)
Record the result: Store the created issue key for the final report.
Error Handling:
--auto): If MCP call fails, report error, ask user if they want to retry or skip. Do NOT retry automatically.--auto): If MCP call fails, log the error, record the candidate as "FAILED" in the report, and continue to the next candidate. Do NOT prompt or retry.Precondition: The JIRA issue must be a Bug in Closed or Verified state (validated in Step 3). If the issue type is not Bug, do not proceed — show an error and re-prompt.
Actions: For each candidate where user chose "reopen":
Get available transitions for the closed issue:
mcp__jira__jira_get_transitions(issue_key="<JIRA-KEY>")
Find the reopen transition: Look for a transition whose name is exactly "To Do", "New", or "Backlog" (case-insensitive). If no suitable transition is found, report the error and ask the user whether to create a new bug instead or skip.
Construct a regression comment describing the new occurrences:
## Regression: issue has resurfaced
This issue was previously closed but the same failure has been detected again in CI.
**Error Signature:** <error_signature>
**Error Severity:** <severity>/5
**Number of affected jobs:** <count>
**Last observed:** <finished date>
**Affected Jobs:**
- [<job_name>](<job_url>)
...
Reopened automatically by /microshift-ci:create-bugs.
Transition the issue to reopen it:
mcp__jira__jira_transition_issue(
issue_key="<JIRA-KEY>",
transition_id="<reopen_transition_id>",
comment="<regression comment>"
)
If the transition call does not support inline comments, add the comment separately:
mcp__jira__jira_add_comment(
issue_key="<JIRA-KEY>",
body="<regression comment>"
)
Record the result: Store the reopened issue key for the final report.
Error Handling:
Precondition: At least one candidate had action create or reopen in Step 4/4a.
After all bugs are created/reopened, update the per-source bug mapping files (<WORKDIR>/analyze-ci-bugs-<source>.json) so that newly created bugs are reflected in the JIRA data consumed by the HTML report generator.
Actions:
Collect new bugs: Gather all candidates where action was create or reopen. For each, record the jira_key, error_signature, and the summary used in creation.
Update each mapping file: For every <WORKDIR>/analyze-ci-bugs-<source>.json file (all sources, not just the current one):
a. Add to open_bugs: Append each new bug to the open_bugs array (skip if the key already exists):
{
"key": "USHIFT-XXXX",
"summary": "MicroShift CI: <error_signature>",
"status": "To Do",
"priority": "Undefined",
"assignee": "Unassigned",
"created": "<today YYYY-MM-DD>",
"updated": "<today YYYY-MM-DD>"
}
b. Add to duplicates: Find the candidate entry in the file's candidates array whose error_signature matches. If found, append the new bug to its duplicates array (skip if the key already exists):
{"key": "USHIFT-XXXX", "summary": "MicroShift CI: <error_signature>", "status": "To Do", "assignee": "Unassigned", "updated": "<today YYYY-MM-DD>"}
c. Write the updated file back to disk.
Skip if no bugs were created: If all candidates were skipped or failed, do not modify mapping files.
Actions:
Ensure <WORKDIR>/analyze-ci-bug-results-<SOURCE_TAG>.json was written in Step 3
Generate the report:
python3 plugins/microshift-ci/scripts/search-bugs.py \
--report <WORKDIR>/analyze-ci-bug-results-<SOURCE_TAG>.json \
--candidates <WORKDIR>/analyze-ci-bug-candidates-merged-<SOURCE_TAG>.json \
--workdir <WORKDIR>
Display the report output to the user
/microshift-ci:create-bugs 4.22
Shows what bugs would be created from release 4.22 analysis without creating anything.
/microshift-ci:create-bugs 4.22 --create
Interactively creates bugs from release 4.22 analysis.
/microshift-ci:create-bugs pr-6396
Shows what bugs would be created from PR #6396 analysis.
/microshift-ci:create-bugs rebase-release-4.22 --create
Resolves the rebase PR for release 4.22, then interactively creates bugs.
/microshift-ci:create-bugs main,4.22,4.21,4.20,5.0
Shows all failures across 5 releases with cross-release dedup applied. Failures appearing in multiple releases are merged into single candidates with Releases: lines.
/microshift-ci:create-bugs main,4.22,4.21,4.20,5.0 --auto --create
Automatically creates bugs across all releases, skipping Jira duplicates. Cross-release duplicates are merged into single bugs referencing all affected releases.
/microshift-ci:create-bugs 4.22 --auto
Shows what auto-decisions would be made (create/skip) without actually creating anything.
/microshift-ci:create-bugs main,4.22 --create
Cross-release dedup is applied, then each merged candidate is presented for interactive action (create/skip/link/reopen).
/microshift-ci:create-bugs 4.19
Error: No job analysis files found at <WORKDIR>/analyze-ci-release-4.19-job-*.txt
Run the analysis first:
/microshift-ci:doctor 4.19
<WORKDIR>analyze-ci-release-<release>-job-*.txt (from /microshift-ci:doctor)analyze-ci-prs-job-*-pr<number>-*.txt (from /microshift-ci:doctor)--create flag triggers interactive mode where each candidate requires user confirmation--auto flag replaces interactive prompts with deterministic auto-decisions (skip if duplicates exist, create otherwise)--auto --create for fully unattended bug creation; --auto alone is a labeled dry-runsearch-bugs.py --merge (even for a single source) to produce a unified output with Jira data injected. Cross-release deduplication uses fuzzy signature matching (token-based Jaccard similarity, 50% threshold)--auto mode, infrastructure failures (failure_type: "infrastructure") are automatically skipped — these are transient CI/cloud issues, not product bugs. Classification uses the same step-name-based logic as the HTML report (classify_breakdown in classify.py). In interactive mode (no --auto), all failures are shown and the user decidesmicroshift-ci-ai-generated for tracking/microshift-ci:prow-jobanalyze-ci-bugs-<source>.json) are written per source in Step 2 (both dry-run and create modes). They serve two purposes: (1) consumed by create-report.py to show JIRA bug links in the HTML report, and (2) consumed by --merge in Step 2a for Jira-based deduplication across releases