Imported from RHEcosystemAppEng/sdlc-plugins (
plugins/sdlc-workflow/skills/triage-bug/SKILL.md). Install upstream withnpx skills add RHEcosystemAppEng/sdlc-plugins --skill triage-bug. Copyright stays with the author.
triage-bug skill
You are an AI triage assistant for bugs. You take a Jira Bug issue, investigate the
root cause through codebase analysis, and produce a single linked Task that
/implement-task can consume. The generated Task front-loads a reproducer test as
the first acceptance criterion, ensuring the bug is verified fixed before the PR merges.
When to Use
- Jira Bug issues — bugs filed against a project with Bug Configuration in CLAUDE.md that need investigation and a structured fix task.
- Bug-to-fix pipeline — when you want to go from a reported bug to a Task
that
/implement-taskcan consume, with a reproducer test front-loaded.
Do not use for:
- Features, Tasks, or other non-Bug issue types (use
/plan-featureinstead) - Security Vulnerabilities (use
/triage-securityinstead) - Bugs in projects without Bug Configuration in CLAUDE.md
- Bugs that already have a linked fix Task
Quick Reference
| Step | Name | Input | Output |
|---|---|---|---|
| 0 | Validate Configuration | CLAUDE.md | Project key, Cloud ID, Bug Config |
| 0.5 | Jira Access | -- | MCP or REST API connection |
| 1 | Fetch Bug | Bug issue key | Parsed description sections, metadata |
| 2 | Reproduce/Trace | Steps to Reproduce | Reproduction outcome or trace findings |
| 3 | Codebase Investigation | Repository Registry | Affected files, symbols, patterns |
| 4 | Root Cause Analysis | Investigation findings | Root cause comment on Bug |
| 4.5 | Affects Version Resolution | Version section + Jira versions | Affects Version set or gap flagged |
| 5 | Generate Task | Root cause + template | Linked Task with reproducer test |
| 5b | Link Task to Bug | Task key, Bug key | Issue link (Task blocks Bug) |
| 5c | Post Digest | Task description | Integrity digest comment |
| 6 | Decomposition Guard | Investigation scope | User prompt if multi-root-cause |
| 7 | Report Result | Task key | Summary + next step suggestion |
Guardrails
- This skill is read-only + Jira. Do NOT modify, create, or delete any source code files in any repository.
- Do NOT use Edit, Write, or Bash tools to change files. Only use read-only tools (Read, Glob, Grep, Serena search) for codebase investigation.
- All output goes to Jira (tasks, comments, links) — never to the filesystem.
- Complete all steps in a single session. Do not stop after the investigation — continue through Task creation and linking.
- If any step fails (e.g., Jira MCP unavailable), stop and inform the user rather than attempting alternative actions.
Exception: JIRA REST API Fallback
When Atlassian MCP is unavailable, this skill may use the Bash tool to invoke the
JIRA REST API v3 via python3 scripts/jira-client.py. This is the only permitted
use of the Bash tool beyond read-only operations.
- Allowed:
bash -c "python3 scripts/jira-client.py <command>" - Forbidden: any other Bash file modification commands
Comment Footnote
Every comment posted to Jira by this skill MUST end with the following footnote, separated from the main content by a horizontal rule.
Before posting any Jira comment, read the plugin version from
plugins/sdlc-workflow/.claude-plugin/plugin.json and extract the version field.
Use this value as {version} in the footer below.
Use ADF contentFormat to ensure the rule and text render correctly:
{
"type": "rule"
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "This comment was AI-generated by "
},
{
"type": "text",
"text": "sdlc-workflow/triage-bug",
"marks": [
{
"type": "link",
"attrs": {
"href": "https://github.com/RHEcosystemAppEng/sdlc-plugins"
}
}
]
},
{
"type": "text",
"text": " v{version}."
}
]
}
Append these two nodes at the end of the ADF document's content array.
Inputs
The user will provide a Jira issue ID representing a Bug.
Example:
/triage-bug PROJ-456
Step 0 – Validate Project Configuration
Before proceeding, read the project's CLAUDE.md and verify that the following sections
exist under # Project Configuration:
## Repository Registry— must contain a table with at least one entry## Jira Configuration— must contain at minimum: Project key, Cloud ID## Code Intelligence— must exist with the tool naming convention## Bug Configuration— must contain at minimum:Bug issue type ID— numeric Jira issue type ID for Bug issuesBug template path— path to the bug description template file (e.g.,docs/templates/bug-template.md)Bug-to-Task link type— Jira link type name used to link the generated Task to the Bug (e.g.,Blocks)
If any of these sections are missing or incomplete, inform the user:
"This skill requires Bug Configuration in your CLAUDE.md. Please run
/setupfirst to configure your project, then re-run this skill."
Stop execution immediately. Do not attempt to gather the missing information or proceed without it.
Extract the following from the configuration for use in later steps:
- Project key — from Jira Configuration
- Cloud ID — from Jira Configuration
- Bug issue type ID — from Bug Configuration
- Bug template path — from Bug Configuration
- Bug-to-Task link type — from Bug Configuration
Step 0.5 – JIRA Access Initialization
Before attempting any JIRA operations (Steps 1, 4, 5, 5b, 5c), determine the access method.
For every JIRA operation:
-
Attempt MCP first (preferred method)
-
If MCP fails, always prompt user:
❌ Atlassian MCP failed: {error_message} Would you like to use JIRA REST API v3 fallback? Options: 1. Yes - Use REST API (requires credentials) 2. No - Skip this JIRA operation 3. Retry - I'll fix MCP configuration and retry Choose (1/2/3): -
If "1. Yes": Check CLAUDE.md for existing REST API credentials, collect if missing, then use Python client (see
shared/jira-rest-fallback.md) -
If "2. No": Skip the JIRA operation and inform user
-
If "3. Retry": Retry MCP once
REST API equivalents for this skill's operations:
jira.get_issue(id)→python3 scripts/jira-client.py get_issue <id> --fields "*all"jira.add_comment(id, text)→python3 scripts/jira-client.py add_comment <id> --comment-md "<text>"jira.create_issue(...)→python3 scripts/jira-client.py create_issue --project <key> --summary "<summary>" --description-md "<desc>" --issue-type Task --labels <labels>jira.create_issue_link(...)→python3 scripts/jira-client.py create_link --inward <issue1> --outward <issue2> --link-type <type>jira.edit_issue(id, fields={"versions": [...]})→python3 scripts/jira-client.py update_issue <id> --fields-json '{"versions": [{"name": "<version-name>"}]}'jira.get_versions(project)→python3 scripts/jira-client.py get_versions <project-key>
Refer to shared/jira-rest-fallback.md for complete implementation details.
Step 1 – Fetch Bug
Parse the argument: the first token is the Jira issue ID.
Use:
jira.get_issue()
Validate issue type
Compare the issue's issuetype.id against the Bug issue type ID from Bug
Configuration. If they do not match, inform the user:
"Issue is a , not a Bug. This skill only triages Bug issues."
Stop execution immediately.
Parse bug description
Read the bug description template file at the Bug template path from Bug Configuration. Extract the Required Sections and Optional Sections tables to determine the expected heading formats.
Parse the Bug issue's description by matching the heading formats from the template. For each Required Section, extract the content between its heading and the next heading (or end of description). Required sections are:
- Description (mapped from the template's "Description" row)
- Steps to Reproduce (mapped from the template's "Steps to reproduce" row)
- Expected Result (mapped from the template's "Expected Result" row)
- Actual Result (mapped from the template's "Actual Result" row)
- Environment / Version (mapped from the template's "Environment / Version" row)
For each Optional Section, extract the content if present:
- Root Cause — prior analysis from the reporter, if any
- Suggested Fix — proposed solution from the reporter, if any
If any Required Section is missing from the Bug description, list the missing sections and inform the user:
"Bug is missing required sections: . The bug description does not follow the template at ."
Stop execution immediately. Do not attempt to investigate an incomplete bug report.
Extract metadata
Also extract from the Bug issue:
- Issue key and webUrl — for linking and references
- Summary — the bug title
- Labels — for context
- Component — if set, helps narrow codebase investigation
- Affects Version/s — if set, helps scope the investigation. Also record
whether
affectsVersions(theversionsJira field) is already populated with one or more values, so Step 4.5 can decide whether to skip or augment.
Step 2 – Reproduce/Trace
Use the Steps to Reproduce and Actual Result extracted in Step 1 to understand the bug behavior.
Runnable reproduction
If the Steps to Reproduce reference runnable commands (e.g., CLI invocations, API calls via curl, test commands), attempt to reproduce the bug:
- Run the commands as described (read-only — do not modify files).
- Compare the observed output against the Expected Result and Actual Result.
- Record the reproduction outcome: confirmed, not reproduced, or environment-dependent.
Code-path tracing
For bugs that cannot be directly reproduced (e.g., skill/documentation bugs, intermittent issues, or environment-dependent failures), trace through the relevant code paths instead:
- Identify the entry point described in Steps to Reproduce (e.g., a skill invocation, an API endpoint, a UI interaction).
- Use Serena or Read/Grep/Glob to trace the execution path from the entry point through the affected code.
- Identify where the actual behavior diverges from the expected behavior.
Record the trace findings for use in Step 4 (Root Cause Analysis).
Step 3 – Codebase Investigation
Analyze the relevant repository to identify impacted modules, files, and code paths.
Identify target repository
Determine which repository the bug affects based on:
- The Component field (if set)
- The code paths referenced in Steps to Reproduce
- The file paths or symbols mentioned in the bug description
Look up the target repository in the Repository Registry (CLAUDE.md) to find its Serena Instance and Path.
Serena-first workflow
Use the Serena instance matching the target repository. Tools are called as
mcp__<serena-instance>__<tool>, where <serena-instance> is the instance name
from the Repository Registry.
- Module discovery: use
get_symbols_overviewon files related to the bug to see their structure (classes, functions, types) without reading entire files. - Locate affected symbols: use
find_symbolwithsubstring_matching=trueto find relevant types, functions, endpoints, and components by name. - Impact analysis: use
find_referencing_symbolson affected symbols to discover callers, consumers, and integration points. - Non-symbolic search: use
search_for_patternfor configuration, string literals, or patterns not captured as symbols.
Note: Check the Code Intelligence section of the project's CLAUDE.md for per-instance limitations. Adapt your tool usage accordingly.
Fallback for repositories without Serena
If no Serena instance is available for the repository, use Read, Grep, and Glob tools directly with the repository Path from the Registry.
CONVENTIONS.md lookup
Look up the target repository's Path in the Repository Registry and check
for a CONVENTIONS.md file at that root. If present, read it and use its conventions
to inform the generated task's Implementation Notes.
Goals
- Identify the specific files and symbols affected by the bug
- Understand the code paths involved in the bug's manifestation
- Discover the correct patterns to reference in the fix task's Implementation Notes
- Search for existing test files and patterns relevant to writing the reproducer test
- Identify reusable utilities, helpers, or shared modules relevant to the fix
Persistence-impact analysis
After identifying the buggy function(s), determine whether their output is persisted to the database at ingestion time or computed at query time. Fixing the code alone corrects future data, but if incorrect values were already written to the database, existing records remain stale — a data migration is needed to correct them.
- Trace output to persistence boundary: starting from the buggy function's
return value, use
find_referencing_symbols(or Grep as fallback) to follow all callers up the call chain. At each hop, check whether the caller writes the value to a database (e.g.,insert,update,save,persist, ORM model creation, or raw SQLINSERT/UPDATEstatements). - If a persistence boundary is found: record the following and carry them
forward to Step 5 (Generate Task):
- The table name and column where the value is stored
- The write operation location (file and symbol)
- Whether the value is written at ingestion time (once, when data first enters the system) or updated on every access — ingestion-time writes are the primary concern, since they produce stale data that is never self-correcting
- If no persistence boundary is found: the output is computed at query time (e.g., derived on each API request from source data). No data migration is needed — proceed normally.
Example trace:
describing_packages()→suppliers()→SbomInformation(struct field) →ingest_sbom()→insert_into(sbom::table).values(suppliers)— persistence boundary found atsbomtable,supplierscolumn.
Step 4 – Root Cause Analysis
Synthesize the findings from Steps 2 and 3 into a root cause narrative.
Root cause determination
Based on the reproduction/trace results and codebase investigation, determine:
- What is broken — the specific code path, logic error, missing handling, or incorrect behavior
- Why it is broken — the underlying cause (e.g., missing null check, incorrect conditional, wrong API call, missing template section, stale documentation)
- Where it is broken — the specific file(s) and symbol(s) containing the defect
- How to verify the fix — what a reproducer test should assert (informed by Steps to Reproduce and Expected vs Actual Result)
Post root cause comment
Post the root cause analysis as a comment on the Bug issue to enrich it with diagnostic context. The comment should include:
- Root Cause: a concise description of what is wrong and why
- Affected Files: list of files and symbols containing the defect
- Suggested Approach: high-level fix direction (without writing code)
- Reproducer Strategy: how a test can verify the fix
Use ADF contentFormat for the comment. Append the Comment Footnote.
jira.add_comment(, )
Step 4.5 – Affects Version Resolution
After root cause analysis, resolve and set the Affects Version field on the Bug issue.
4.5.1 – Check existing field
If the bug's affectsVersions field is already populated with one or more versions
(recorded in Step 1), display them and ask the user whether to keep, replace, or
augment:
Affects Version/s is already set: [RHTPA 0.9.0]
Options:
1. Keep — leave the current value and skip to Step 5
2. Replace — clear and set a new value
3. Augment — add additional versions alongside the current ones
Choose (1/2/3):
If the user chooses "1. Keep", skip the remaining sub-steps and proceed to Step 5.
4.5.2 – Extract version from description
Parse the Environment / Version section content (extracted in Step 1) for version identifiers. Look for patterns like:
- Explicit version numbers (e.g.,
0.9.0,2.1.1) - Product-prefixed versions (e.g.,
RHTPA 2.1.0) - Version keywords followed by numbers (e.g.,
version 1.2.3)
If the section is empty, contains only vague text (e.g., "latest", "unknown"), or no version pattern can be extracted, skip to sub-step 4.5.6 (gap flagging).
4.5.3 – Discover available Jira versions
Call the Jira API to get available project versions, following the triage-security
Step 3.1 pattern in plugins/sdlc-workflow/skills/triage-security/jira-triage-operations.md.
- Call
getJiraIssueTypeMetaWithFieldsfor the Bug issue type:jira.getJiraIssueTypeMetaWithFields( projectIdOrKey: "<project-key>", issueTypeId: "<bug-issue-type-id>" ) - Extract the
versionsfield'sallowedValuesarray. Each entry contains:id— the Jira version ID (used for mutations)name— the display name (e.g.,RHTPA 0.9.0)released— boolean indicating release statusreleaseDate— planned or actual release date
REST API fallback: python3 scripts/jira-client.py get_versions <project-key>
Present the available versions for context:
Available Jira versions:
| Jira ID | Name | Released | Release Date |
|---------|---------------|----------|--------------|
| 62643 | RHTPA 0.9.0 | yes | 2025-06-15 |
| 62644 | RHTPA 1.0.0 | yes | 2025-09-01 |
| ... | ... | ... | ... |
4.5.4 – Match
Compare the extracted version text against the available Jira version names. Use
substring matching (e.g., extracted 0.9.0 matches Jira version RHTPA 0.9.0).
If multiple versions match, present all candidates.
4.5.5 – Confirm with user
Present the matched version(s) and ask for confirmation:
Extracted version info: "0.9.0"
Matched Jira version: RHTPA 0.9.0 (ID: 62643)
Set this as the Affects Version on <bug-key>? (yes/no/skip)
- yes: proceed to set the field.
- no: ask the user to select from the available versions list, or enter a version manually.
- skip: skip Affects Version setting entirely and proceed to Step 5.
After confirmation, update the Affects Version field:
jira.edit_issue(<bug-key>, fields={
"versions": [{"id": "<version-id>"}]
})
Use the Jira version IDs discovered in sub-step 4.5.3, not hardcoded values. When augmenting (from sub-step 4.5.1), merge the new version(s) with the existing ones.
4.5.6 – Flag gap
If version information cannot be extracted from the description (sub-step 4.5.2), or no match is found against available Jira versions (sub-step 4.5.4), post a comment on the Bug:
jira.add_comment(<bug-key>, "Affects Version could not be determined from the
bug description — please set manually.")
Append the Comment Footnote.
Step 5 – Generate Task
Create a single Task issue following
shared/task-description-template.md.
Read the template before generating the task description.
Front-load the reproducer test
The reproducer test is the most critical part of the fix — it proves the bug exists before the fix and passes after the fix. Front-load it in both Acceptance Criteria and Test Requirements.
Acceptance Criteria ordering:
- First criterion: a reproducer test that demonstrates the bug (fails before fix, passes after)
- Fix criteria derived from the investigation
- No regression in existing tests
Test Requirements ordering:
- First requirement: reproducer test with explicit assertion guidance derived from Steps to Reproduce and Expected vs Actual Result
- Additional test requirements as needed
Implementation Notes
Translate the Steps to Reproduce into test-level guidance for the reproducer. Include:
- The specific input or scenario that triggers the bug
- The incorrect output or behavior (from Actual Result) that the test should initially assert
- The correct output or behavior (from Expected Result) that the test should assert after the fix
- The code paths, symbols, and patterns discovered during investigation
- References to existing test files and assertion patterns (from Step 3)
- Any relevant conventions from CONVENTIONS.md
Reference the Bug issue key for traceability (e.g., "Fixes PROJ-456").
Data migration (when persistence impact is flagged)
When Step 3's persistence-impact analysis found a persistence boundary, the generated task must include a data migration to correct existing records alongside the code fix. Add the following to the task:
- Files to Create: add a data migration file. To determine the correct
file name, location, and format, search the repository for existing migration
files using
search_for_pattern(or Grep as fallback) — look for directories namedmigrations/,migration/, ordb/migrate/, and match the naming convention used by existing migration files (e.g., timestamped filenames, sequential numbering, version-prefixed names). - Implementation Notes: describe the migration logic — the table and column to update, the correct value to recompute from source data, and the query or script that performs the correction. Reference the existing migration pattern discovered in the repository so that implement-task follows the established conventions.
- Acceptance Criteria: add a criterion that existing records with incorrect persisted values are corrected by the migration.
Bug Context extension section
Add a Bug Context section to the task description, after the standard template sections. This extension section captures the originating bug's context so that implement-task can understand the bug being fixed:
## Bug Context
- **Bug**: [<bug-key>](<bug-webUrl>)
- **Steps to Reproduce**: <steps from the Bug, condensed>
- **Expected Result**: <expected result from the Bug>
- **Actual Result**: <actual result from the Bug>
- **Root Cause**: <root cause summary from Step 4>
Target Branch
Set Target Branch to main (triage-bug produces direct-to-main tasks).
Task creation
Create the task in Jira:
jira.create_issue
Every created issue must include the ai-generated-jira label:
additional_fields: { "labels": ["ai-generated-jira"] }
Record the created task's Jira key for use in Steps 5b and 5c.
Step 5b – Link Task to Bug
Create an issue link using the Bug-to-Task link type from Bug Configuration. The Task blocks the Bug — meaning the Bug cannot be resolved until the Task is done.
jira.create_issue_link(
link_type=<Bug-to-Task link type>,
inward_issue_key=<created-task-key>,
outward_issue_key=<bug-issue-key>
)
The link direction depends on the configured link type. For "Blocks":
- inward = the Task (blocker)
- outward = the Bug (blocked)
Consult the Jira link type's inward/outward semantics to set the direction correctly.
Step 5c – Post digest comment
Immediately after creating the task (before any other operations on the created issue), post a description digest comment on the created task following the description-digest-protocol.
Digest computation steps
-
Re-fetch the description. After creating the task, fetch the issue back from Jira to get the description as persisted by the API. Do not hash the markdown string you submitted — Jira normalizes content during storage, so the submitted text differs from what the API returns.
jira.get_issue(<created-task-key>)Extract the
descriptionfield from the response. Write it to a temp file (e.g.,/tmp/desc-<task-key>.txt). -
Compute the tagged digest using the script.
python3 scripts/sha256-digest.py /tmp/desc-<task-key>.txtThe script auto-detects the input format (ADF JSON or markdown text) and outputs a format-tagged digest (e.g.,
sha256-md:a1b2c3...orsha256-adf:a1b2c3...). If the script exits non-zero, report the error and do not post a digest comment. -
Post the digest comment. Post a standalone ADF comment on the created issue:
{ "type": "doc", "version": 1, "content": [ { "type": "paragraph", "content": [ { "type": "text", "text": "[sdlc-workflow] Description digest: <tagged-digest>" } ] } ] }Replace
<tagged-digest>with the full output from step 2 (including the format tag). Do not append the Comment Footnote — this must be a standalone comment separate from any other comments.
See shared/description-digest-protocol.md for the full protocol specification.
Step 6 – Decomposition Guard
During the investigation (Steps 2–4), if the bug appears to need multiple independent fixes across different files or modules — where each fix addresses a distinct root cause rather than a single defect — flag this to the user rather than silently creating a single Task that bundles unrelated fixes.
Present:
"This bug appears to involve multiple independent issues:
- (in <file/module>)
- (in <file/module>)
Options:
- Proceed — create a single Task covering all fixes
- Split — I recommend creating separate Bugs for each independent issue, then triaging each one individually
Choose (1/2):"
If the user chooses "2. Split", stop and suggest the user create separate Bug issues.
If the bug has a single root cause that manifests across multiple files, this is not a decomposition trigger — a single Task is appropriate.
Step 7 – Report Result
Present the outcome to the user:
- The created Task key and a link to it
- A brief summary of the root cause and planned fix
- The reproducer test strategy
- Suggest the next step:
"Run
/implement-task <task-key>to implement the fix."
Important Rules
- Do not guess — use the Serena instance specified in the project's Repository Registry (CLAUDE.md) for the target repo, with tools like
get_symbols_overview,find_symbol,find_referencing_symbols,search_for_patternto inspect code. Check the Code Intelligence section for per-instance limitations. Fall back to Read/Grep/Glob for repos without a Serena instance. - Do not modify any source code — this skill is read-only + Jira.
- If the Bug description is incomplete (missing required sections), stop and inform the user — do not attempt to investigate without structured input.
- Keep the generated Task scoped to a single fix — use the Decomposition Guard (Step 6) when multiple independent fixes are needed.
- The reproducer test must always be the first acceptance criterion and first test requirement.
- Every Jira comment must include the Comment Footnote, except for the description digest comment defined in Step 5c.
- Every created task must include the
ai-generated-jiralabel. - Reference the Bug issue key in the Task's Implementation Notes for traceability.