Skip to content

Commit b99e0be

Browse files
Merge pull request #6420 from ggiguash/ci-doctor-bug-correlation
USHIFT-6764: Introduce bug correlation in analyze-ci:doctor reports
2 parents 67116d2 + 90f1805 commit b99e0be

5 files changed

Lines changed: 162 additions & 43 deletions

File tree

.claude/commands/analyze-ci/create-bugs.md

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ allowed-tools: Bash, Read, Write, Glob, Grep, Agent, mcp__jira__jira_search, mcp
1313
```
1414

1515
## Description
16-
Reads individual job analysis reports produced by `analyze-ci:release` or `analyze-ci:pull-requests` and creates JIRA bugs in USHIFT for legitimate 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.
16+
Reads individual job analysis reports produced by `analyze-ci:release` or `analyze-ci:pull-requests` 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.
1717

1818
This command does NOT re-analyze CI jobs. It consumes existing job analysis files from `${WORKDIR}/`.
1919

@@ -122,23 +122,14 @@ WORKDIR=/tmp/analyze-ci-claude-workdir.$(date +%y%m%d)
122122
}
123123
```
124124

125-
### Step 3: Filter Out Non-Bug-Worthy Failures
125+
### Step 3: Pass All Failures Through
126126

127127
**Actions**:
128-
1. Remove entries where `SEVERITY <= 2` (minor/flaky issues)
129-
2. Remove entries where `INFRASTRUCTURE_FAILURE=true` **AND** the failure is transient/external (not a CI configuration bug). Use `STACK_LAYER` to distinguish:
130-
- **Filter out** (transient infrastructure — out of our control):
131-
- `STACK_LAYER` contains `AWS Infra` (AWS quota, VM creation, networking)
132-
- `STACK_LAYER` contains `External Infrastructure` (container registry outages, third-party services)
133-
- `STACK_LAYER` is `build phase` (release image import timeouts, registry 404s)
134-
- **Keep as bug-worthy** (CI configuration issues — need code fixes):
135-
- `STACK_LAYER` is `test setup phase`, `Test Configuration`, or similar (missing test files, wrong directory mappings, broken scenario selection logic)
136-
- Any `INFRASTRUCTURE_FAILURE=true` entry whose analysis text indicates the fix requires a code change in the repository (e.g., adding missing files, updating directory mappings, fixing CI scripts)
137-
3. Log each filtered-out entry with reason. For kept CI configuration failures, log them as `"CI CONFIG (kept)"` to distinguish from product test failures.
128+
1. All parsed entries from Step 2 proceed directly to Step 4 without any filtering
129+
2. Do NOT skip entries based on severity, infrastructure failure status, or stack layer
130+
3. Log the total number of entries proceeding to deduplication
138131

139-
**Rationale**: Not all "infrastructure failures" are transient. A missing test scenario directory or broken CI script mapping is a configuration bug that requires a code fix — these should result in JIRA bugs, not be silently filtered out. Only truly external/transient failures (AWS outages, registry issues, image import timeouts) should be excluded.
140-
141-
**Output**: Filtered list of bug-worthy failures with count of filtered entries reported to user.
132+
**Output**: Complete list of all parsed failures proceeding to deduplication.
142133

143134
### Step 4: Deduplicate by ERROR_SIGNATURE
144135

@@ -216,15 +207,14 @@ If results are found, fetch their details with `mcp__jira__jira_get_issue` and f
216207
DRY-RUN SUMMARY
217208
Source: <SOURCE_LABEL>
218209
Total job files parsed: N
219-
Filtered out (infra/low severity): N
220210
Unique bug candidates: N
221211
Candidates with potential duplicates: N
222212
Candidates ready to file: N
223213
224214
To create these bugs, run:
225215
/analyze-ci:create-bugs <source> --create
226216
```
227-
- Do NOT prompt for any actions. Do NOT create any issues. Stop here.
217+
- Do NOT prompt for any actions. Do NOT create any issues. Do NOT proceed to Steps 7/7a (create/reopen). Continue to Step 7b and Step 8.
228218
229219
3. **In create mode** (`--create` specified):
230220
- For each candidate, prompt the user:
@@ -387,6 +377,38 @@ For each candidate where user chose "reopen":
387377
- If the transition fails, report error and ask user if they want to retry, create a new bug instead, or skip
388378
- Do NOT retry automatically
389379

380+
### Step 7b: Write Machine-Readable Bug Mapping File
381+
382+
**Actions**:
383+
After processing all bug candidates (Steps 5-7a) and regardless of mode (dry-run or create), write a machine-readable bug mapping file that `analyze-ci:create-report` can consume to display JIRA bug links in the HTML report. The file content is based on the Jira search results from Step 5 — it is not affected by whether bugs were created or reopened in Steps 7/7a.
384+
385+
1. Save to `${WORKDIR}/analyze-ci-bugs-<source>.txt` (overwrite if exists)
386+
2. Use this structured format with one block per bug candidate:
387+
388+
```text
389+
--- BUG MAPPING ---
390+
SOURCE: <source>
391+
DATE: YYYY-MM-DD
392+
--- BUG CANDIDATE ---
393+
ERROR_SIGNATURE: <error_signature>
394+
SEVERITY: <N>
395+
STEP_NAME: <step_name>
396+
AFFECTED_JOBS: <count>
397+
JIRA_DUPLICATES: <comma-separated list of JIRA keys, or "None">
398+
JIRA_DUPLICATE_DETAILS: <KEY>|<summary>|<status>, <KEY>|<summary>|<status>, ...
399+
JIRA_REGRESSIONS: <comma-separated list of JIRA keys, or "None">
400+
JIRA_REGRESSION_DETAILS: <KEY>|<summary>|<status>, <KEY>|<summary>|<status>, ...
401+
--- END BUG CANDIDATE ---
402+
--- BUG CANDIDATE ---
403+
...
404+
--- END BUG CANDIDATE ---
405+
--- END BUG MAPPING ---
406+
```
407+
408+
3. **IMPORTANT**: This file must be written in BOTH dry-run and create modes. The file enables `analyze-ci:create-report` to show linked bugs per issue in the HTML report.
409+
4. The `JIRA_DUPLICATE_DETAILS` and `JIRA_REGRESSION_DETAILS` lines provide summary and status for each JIRA key, pipe-separated within each entry and comma-separated between entries. If no duplicates/regressions, use "None".
410+
5. Save using a Bash heredoc to avoid Write tool permission issues with `/tmp` paths.
411+
390412
### Step 8: Generate Results Report
391413

392414
**Actions**:
@@ -407,9 +429,7 @@ PARSING
407429
Skipped (no structured summary): N
408430
409431
FILTERING
410-
Infrastructure failures removed: N
411-
Low severity (<=2) removed: N
412-
Bug-worthy failures: N
432+
None (all failures included)
413433
414434
DEDUPLICATION
415435
Unique bug candidates: N
@@ -525,12 +545,12 @@ Run the analysis first:
525545
- PR jobs: `analyze-ci-prs-job-*-pr<number>-*.txt` (from `/analyze-ci:pull-requests`)
526546
- Dry-run is the default to prevent accidental bug creation
527547
- The `--create` flag triggers interactive mode where each candidate requires user confirmation
528-
- Transient infrastructure failures (AWS, VM creation, quota, registry outages) are automatically filtered out
529-
- CI configuration failures (missing test files, broken directory mappings) are kept as bug-worthy even if marked as INFRASTRUCTURE_FAILURE=true
548+
- All failures are included without filtering — no entries are skipped based on severity, infrastructure status, or stack layer
530549
- Bugs are created in USHIFT with component "MicroShift"; duplicate search covers both USHIFT and OCPBUGS
531550
- All created bugs are labeled with `microshift-ci-ai-generated` for tracking
532551
- Security level is set to "Red Hat Employee" on all created issues
533552
- The STRUCTURED SUMMARY block in job files is required — this is a contract with `/analyze-ci:prow-job`
553+
- In addition to the text report, a machine-readable bug mapping file (`analyze-ci-bugs-<source>.txt`) is written in both dry-run and create modes — this file is consumed by `analyze-ci:create-report` to show JIRA bug links in the HTML report
534554

535555
## Related Skills
536556

.claude/commands/analyze-ci/create-report.md

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ allowed-tools: Bash, Read, Write, Glob, Grep
1313
```
1414

1515
## Description
16-
Reads analysis output files from `${WORKDIR}/` (produced by `analyze-ci:release` and `analyze-ci:pull-requests`) and generates a single consolidated HTML report with tabbed navigation. This command does NOT run any CI analysis — it only reads existing files and generates HTML.
16+
Reads analysis output files from `${WORKDIR}/` (produced by `analyze-ci:release` and `analyze-ci:pull-requests`) and optionally bug mapping files (produced by `analyze-ci:create-bugs` in both dry-run and create modes) to generate a single consolidated HTML report with tabbed navigation. When bug mapping files are present, the report shows linked JIRA bugs per issue. This command does NOT run any CI analysis — it only reads existing files and generates HTML.
1717

1818
## Arguments
1919
- `$ARGUMENTS` (required): Comma-separated list of release versions that were analyzed (e.g., `4.19,4.20,4.21,4.22`)
@@ -38,12 +38,15 @@ WORKDIR=/tmp/analyze-ci-claude-workdir.$(date +%y%m%d)
3838
- `${WORKDIR}/analyze-ci-release-<version>-summary.*.txt`
3939
6. Find the PR summary file:
4040
- `${WORKDIR}/analyze-ci-prs-summary.*.txt`
41-
7. Report what was found:
41+
7. Find bug mapping files (produced by `analyze-ci:create-bugs` in both dry-run and create modes):
42+
- `${WORKDIR}/analyze-ci-bugs-*.txt`
43+
- These are optional — the report works without them, but when present, JIRA bug links are shown per issue
44+
8. Report what was found:
4245
```text
4346
Files discovered:
44-
Release 4.19: summary found
45-
Release 4.20: summary found
46-
PRs: summary found
47+
Release 4.19: summary found, bug mapping found
48+
Release 4.20: summary found, bug mapping found
49+
PRs: summary found, bug mapping found (or "no bug mapping")
4750
```
4851

4952
**Error Handling**:
@@ -58,7 +61,13 @@ WORKDIR=/tmp/analyze-ci-claude-workdir.$(date +%y%m%d)
5861
3. If a summary file is missing for a release, note "Analysis failed or produced no output"
5962
4. If no PR summary file exists, note "No open rebase PRs or no failures found"
6063

61-
**Important**: Only summary files are read — per-job files are NOT read. The summary files contain all information needed for the HTML report: severity levels, job names, Prow URLs, finish dates (in `[YYYY-MM-DD]` format after each job name), root cause descriptions, and affected scenarios. This keeps context usage minimal.
64+
**Important**: Only summary files and bug mapping files are read — per-job files are NOT read. The summary files contain all information needed for the HTML report: severity levels, job names, Prow URLs, finish dates (in `[YYYY-MM-DD]` format after each job name), root cause descriptions, and affected scenarios. This keeps context usage minimal.
65+
66+
5. If bug mapping files were found, read and parse them:
67+
- Each file contains `--- BUG CANDIDATE ---` blocks with `ERROR_SIGNATURE`, `JIRA_DUPLICATES`, `JIRA_DUPLICATE_DETAILS`, `JIRA_REGRESSIONS`, and `JIRA_REGRESSION_DETAILS` fields
68+
- Build a lookup structure mapping error signatures to their JIRA bug information
69+
- The bug mapping file for a release is `analyze-ci-bugs-<version>.txt`; for rebase PRs it is `analyze-ci-bugs-rebase-release-<version>.txt`
70+
- This data will be used in Step 3 to show JIRA links per issue in the HTML
6271

6372
### Step 3: Generate HTML Report
6473

@@ -116,6 +125,12 @@ The HTML file must be a self-contained, single-file document with embedded CSS a
116125
.job-date { font-weight: 400; color: #6c757d; font-size: 0.85em; }
117126
.collapsible-content { display: none; }
118127
.collapsible-content.show { display: block; }
128+
.bug-links { margin: 8px 0; padding: 8px 12px; background: #f0f4ff; border-left: 3px solid #0366d6; font-size: 0.9em; }
129+
.bug-links .bug-tag { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 0.85em; font-weight: 600; margin: 2px 4px 2px 0; text-decoration: none; }
130+
.bug-tag-open { background: #fff3cd; color: #856404; border: 1px solid #ffc107; }
131+
.bug-tag-regression { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
132+
.bug-links-label { font-weight: 600; color: #495057; margin-right: 6px; }
133+
.no-bugs { color: #6c757d; font-style: italic; font-size: 0.85em; }
119134
.toc { background: white; border-radius: 8px; padding: 20px; margin: 20px 0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
120135
.toc ul { list-style: none; padding-left: 0; }
121136
.toc li { padding: 5px 0; }
@@ -180,6 +195,24 @@ The HTML file must be a self-contained, single-file document with embedded CSS a
180195
<div class="collapsible">1. SEVERITY: Issue Title (N jobs)</div>
181196
<div class="collapsible-content">
182197
<div class="root-cause"><strong>Root Cause:</strong> Root cause description from summary</div>
198+
<!-- Bug links from bug mapping file (if available) -->
199+
<!-- Match issue title/error signature against ERROR_SIGNATURE values using token-overlap matching: -->
200+
<!-- 1. Normalize both strings: lowercase, strip punctuation, remove stopwords (a, an, the, is, in, of, to, for, and, or, with, that, this, from, on, at, by) -->
201+
<!-- 2. Simple stemming: strip common suffixes (-ing, -ed, -s, -tion, -ment, -ness, -ly, -er, -est) -->
202+
<!-- 3. Extract distinctive tokens (tool names, error codes, test IDs, paths, numeric identifiers) -->
203+
<!-- 4. Compute token overlap: intersection of token sets; coverage = overlap_count / min(set_a_size, set_b_size) -->
204+
<!-- 5. Match if: (a) exact substring match of either full string within the other (highest confidence=1.0), OR (b) ≥3 distinctive tokens overlap, OR (c) token coverage ≥60% -->
205+
<!-- 6. Confidence score = overlap_count / min(set_a_size, set_b_size); ties broken by highest confidence, then longest ERROR_SIGNATURE -->
206+
<!-- 7. Each issue matches at most one bug candidate (the highest-confidence match) -->
207+
<div class="bug-links">
208+
<span class="bug-links-label">JIRA Bugs:</span>
209+
<!-- For each matching JIRA duplicate (open bugs): -->
210+
<a class="bug-tag bug-tag-open" href="https://issues.redhat.com/browse/USHIFT-XXXXX" title="Bug summary text [Status]">USHIFT-XXXXX</a>
211+
<!-- For each matching JIRA regression (closed bugs): -->
212+
<a class="bug-tag bug-tag-regression" href="https://issues.redhat.com/browse/USHIFT-YYYYY" title="Bug summary text [Closed] regression">USHIFT-YYYYY ⟲</a>
213+
<!-- If no matching bugs found in bug mapping: -->
214+
<span class="no-bugs">No tracked bugs</span>
215+
</div>
183216
<p><strong>Affected Jobs:</strong></p>
184217
<ul>
185218
<!-- Each job link includes finish date from the grep extraction -->
@@ -208,14 +241,29 @@ The HTML file must be a self-contained, single-file document with embedded CSS a
208241
<div class="collapsible-content">
209242
<p><strong>Job:</strong> <span class="job-date">[YYYY-MM-DD]</span> <a href="JOB_URL">job-name</a></p>
210243
<div class="root-cause"><strong>Root Cause:</strong> Root cause from PR summary</div>
244+
<!-- Bug links from bug mapping file (if available for this rebase PR) -->
245+
<!-- Match job root cause/error description against ERROR_SIGNATURE values using the same token-overlap algorithm described in the Periodics section above -->
246+
<div class="bug-links">
247+
<span class="bug-links-label">JIRA Bugs:</span>
248+
<a class="bug-tag bug-tag-open" href="https://issues.redhat.com/browse/USHIFT-XXXXX" title="Bug summary text [Status]">USHIFT-XXXXX</a>
249+
<a class="bug-tag bug-tag-regression" href="https://issues.redhat.com/browse/USHIFT-YYYYY" title="Bug summary text [Closed] regression">USHIFT-YYYYY ⟲</a>
250+
<span class="no-bugs">No tracked bugs</span>
251+
</div>
211252
</div>
212253
</div>
213254

214255
<!-- If no rebase PRs found -->
215256
<div class="release-section">
216257
<p>No open rebase pull requests found.</p>
217258
</div>
259+
218260
</div>
261+
262+
<!-- Extra spacing to ensure tab content is fully visible in Prow Spyglass iframe -->
263+
<p>&nbsp;</p>
264+
<p>&nbsp;</p>
265+
<p>&nbsp;</p>
266+
<p>&nbsp;</p>
219267
</div>
220268

221269
<script>
@@ -248,6 +296,19 @@ document.querySelectorAll('.collapsible').forEach(function(el) {
248296
- Do NOT re-analyze or reinterpret the data — use summary file content as-is
249297
- Convert the plain text summary reports into HTML-formatted content, preserving all information
250298
- Ensure all Prow job URLs from the summaries remain clickable links in the HTML
299+
- **Bug Correlation**: For each issue in the TOP ISSUES section (Periodics tab) and each failed job entry (Pull Requests tab), attempt to match it against the bug candidates from the corresponding bug mapping file (`analyze-ci-bugs-<release>.txt` for releases, `analyze-ci-bugs-rebase-release-<version>.txt` for rebase PRs). Match by comparing the issue title/description or job root cause against the `ERROR_SIGNATURE` in each `--- BUG CANDIDATE ---` block using this token-overlap algorithm:
300+
1. **Normalize** both strings: lowercase, strip punctuation, remove stopwords (`a, an, the, is, in, of, to, for, and, or, with, that, this, from, on, at, by`)
301+
2. **Simple stemming**: strip common suffixes (`-ing, -ed, -s, -tion, -ment, -ness, -ly, -er, -est`)
302+
3. **Extract distinctive tokens**: tool names, error codes, test IDs, paths, numeric identifiers
303+
4. **Compute token overlap**: intersection of token sets; coverage = `overlap_count / min(set_a_size, set_b_size)`
304+
5. **Match criteria**: (a) exact substring match of either full string within the other (highest confidence=1.0), OR (b) ≥3 distinctive tokens overlap, OR (c) token coverage ≥60%
305+
6. **Confidence score** = `overlap_count / min(set_a_size, set_b_size)`; ties broken by highest confidence, then longest `ERROR_SIGNATURE`
306+
7. Each issue matches at most one bug candidate (the highest-confidence match)
307+
When a match is found:
308+
- Show `JIRA_DUPLICATES` as clickable links with `bug-tag-open` styling (linking to `https://issues.redhat.com/browse/<KEY>`) with the summary from `JIRA_DUPLICATE_DETAILS` as the title attribute
309+
- Show `JIRA_REGRESSIONS` as clickable links with `bug-tag-regression` styling (with ⟲ suffix) with the summary from `JIRA_REGRESSION_DETAILS` as the title attribute
310+
- If no bug mapping file exists for a release, or no candidates match an issue, show `<span class="no-bugs">No tracked bugs</span>`
311+
- If the bug mapping file exists but a candidate has `JIRA_DUPLICATES: None` and `JIRA_REGRESSIONS: None`, show `<span class="no-bugs">No tracked bugs</span>`
251312
- Use appropriate badge colors:
252313
- `badge-ok`: 0 failed jobs
253314
- `badge-issues`: 1+ failed jobs
@@ -303,7 +364,7 @@ HTML report generated: ${WORKDIR}/microshift-ci-doctor-report.html
303364
- **analyze-ci:doctor**: Orchestrator that runs analyses and then invokes this command
304365
- **analyze-ci:release**: Per-release periodic job analysis (produces the input files)
305366
- **analyze-ci:pull-requests**: PR job analysis (produces the input files)
306-
- **analyze-ci:create-bugs**: Creates JIRA bugs from analysis output
367+
- **analyze-ci:create-bugs**: Creates JIRA bugs from analysis output; also produces bug mapping files (`analyze-ci-bugs-<source>.txt`) consumed by this command to show JIRA links in the HTML report
307368

308369
## Notes
309370
- This command is read-only — it only reads existing analysis files and generates HTML
@@ -312,3 +373,4 @@ HTML report generated: ${WORKDIR}/microshift-ci-doctor-report.html
312373
- If analysis files are missing for a release, it is noted in the report but does not block generation
313374
- If no PR files exist, the Pull Requests tab shows "No open rebase pull requests found"
314375
- This command can be re-run to regenerate the HTML without re-running the analyses
376+
- If bug mapping files (`analyze-ci-bugs-*.txt`) exist in `${WORKDIR}/`, JIRA bug links are shown per issue in the HTML; if not, the report still generates correctly without bug links

0 commit comments

Comments
 (0)