en un clic
jira-triage
// Fetch Jira issues by filter criteria, define structured triage operations, and bulk-apply them. Full Triage mode orchestrates all analysis skills on New issues end-to-end.
// Fetch Jira issues by filter criteria, define structured triage operations, and bulk-apply them. Full Triage mode orchestrates all analysis skills on New issues end-to-end.
Evaluates code changes in a PR against the acceptance criteria of a Jira issue. Given a Jira issue key, determines whether each criterion is satisfied, partially satisfied, or missed. Use when asked to evaluate a PR against a Jira issue, check acceptance criteria, or verify implementation completeness.
Builds and deploys the gen-ai sidecar container to a personal OpenShift cluster for testing. Handles frontend build, Go BFF cross-compile, Podman image build/push to quay.io, and deployment patching. Supports both initial deploy and fast iteration (rebuild + pod delete). Use when the user wants to test local gen-ai changes on a real (dev) cluster.
Assign a scrum team label to issues based on area-to-scrum mapping and team keyword associations, producing ADD_LABEL operations in the standard triage format.
Validate and assign dashboard-area-* labels on Jira issues using multi-signal analysis. Tag mode assigns missing labels; Validate mode audits existing labels for correctness.
Pre-merge readiness check for a PR or local branch. Gathers context, runs reviews and checks, reports a results table. Interactive by default — asks what to review and whether to fix. Supports flags: --fix, --local, --review X,Y, --skip-review X,Y, --ci, --help.
Sync upstream changes for a package and open a PR. Pass a package name as the first argument (e.g. model-registry or notebooks) or be prompted to choose. Optionally pass a PR URL to do a temporary test sync.
| name | jira-triage |
| description | Fetch Jira issues by filter criteria, define structured triage operations, and bulk-apply them. Full Triage mode orchestrates all analysis skills on New issues end-to-end. |
Composable infrastructure for Jira triage automation. Provides three capabilities -- Fetch, Apply, and Full Triage -- connected by a standard operations format. Analysis logic lives in separate skills; Full Triage orchestrates them end-to-end.
Before any Fetch, Apply, or Full Triage operation, read persona.md and jira-project-reference.md. The persona defines the execution protocol (thoroughness, verification, no assumptions). The project reference provides all Jira-specific values (project key, component, field IDs, labels, scrum-to-area mapping). This skill contains no hardcoded Jira values.
Fresh Start Rule: Every invocation starts from scratch with fresh Jira queries and fresh analysis. Do not read, scan, or reference previous operation files in
.artifacts/triage/unless the user explicitly provides a file path to apply or resume. Seepersona.md§ Fresh Start for the full rule.
User
│
├─── Fetch ───────────── return issue set (in-session)
│
├─── Apply ───────────── validate, dry-run, execute
│
└─── Full Triage ─────── orchestrate analysis pipeline for New issues
│
┌────┴────┐
│ Fetch │ Query New issues (lightweight: no descriptions)
└────┬────┘
│ issue keys + summaries (small context footprint)
┌────┴──────────────────────────────────────────┐
│ Per-Issue Loop │
│ (one issue at a time, write results to disk) │
│ │
│ 0. Fetch full issue details (description etc) │
│ │
│ a. Team Gate │
│ Dashboard-owned? → assign team, continue │
│ Another team / not ours? → STOP (skip) │
│ │
│ b. Analysis Pipeline │
│ 1. validate-issue-type │
│ 2. validate-description │
│ 3. validate-priority-severity │
│ 4. evaluate-blockers (Tag mode) │
│ 5. validate-area-label (Tag mode) │
│ 6. assign-scrum-team │
│ │
│ c. Status Transition (opt-in only) │
│ Skipped unless user requests transitions │
│ No blockers → TRANSITION to Backlog │
│ Has blockers → remain in New │
│ │
│ d. Write operations to file + report progress │
└────┬──────────────────────────────────────────┘
│ operations file on disk (built incrementally)
┌────┴────┐
│ Apply │ Read file, validate, dry-run/execute,
│ │ post triage comments, report
└─────────┘
Entry points:
Fetch and Apply can be used independently. A user might Fetch without Apply (just exploring), or Apply a hand-edited operations file without Fetch. Full Triage chains them together with the analysis pipeline.
When processing multiple issues through any analysis skill, follow these principles to avoid exhausting the agent's context window:
{ key, summary, blocking signals } for each issue -- compact enough to hold the entire set in context. Every issue from every page must appear in the inventory. Compare the inventory count against the total returned by the API. If they don't match, re-read the search results until they do. Never estimate, approximate, or skip issues from the search results. Bulk search responses can be thousands of lines; reading them in chunks is fine, but every chunk must be fully processed and every issue key extracted. Missing even one issue invalidates the entire batch.description). This gives you keys, summaries, and metadata without loading heavy content.jira_get_issue only when processing that specific issue. Discard the details after that issue is complete.[7/53]) so the user can confirm all issues are being processed.Default: inline processing. All issues are processed in the current agent context using principles 1–5 above. No sub-agents are launched. This keeps everything in a single conversation with full shared context.
Opt-in: sub-agent delegation ("think deep"). When the user explicitly requests deeper analysis (e.g., "think deep", "use sub-agents"), delegate per-issue processing to sub-agents via the Task tool. Each sub-agent starts with a fresh context window — no accumulated API responses, no residual skill file content from previous issues. The parent agent holds only the inventory and coordinates dispatch. Sub-agents are dispatched sequentially (one at a time). See Capability 3 § Sub-Agent Delegation for the full pattern.
Full Triage implements these principles end-to-end (see Capability 3 § Context Management). Standalone skill invocations (e.g., "validate area labels on 30 backlog issues") should follow the same pattern: lightweight fetch for the list, per-issue jira_get_issue for details, per-issue write to the operations file. When the user opts into sub-agent delegation for standalone batch operations, apply the same pattern documented under Full Triage.
Context compaction silently degrades the agent's working memory. After compaction, issue summaries are paraphrased or hallucinated, issue counts drift, skipped issues vanish, and operations get fabricated from vague recollection. The operations file on disk is the only reliable state. These rules are non-negotiable:
After Step 1 creates the initial skeleton (with the verified issues map and issueCount), the file is the authoritative record. Never regenerate, reconstruct, or rewrite the file from memory. All subsequent writes are read-append-write: read the current file from disk, append new operations for the just-completed issue, write it back. If your memory conflicts with the file on disk, the file wins.
If the operations file already exists and contains data, treat its issues map and existing operations array as immutable history. Only append to the operations array. Never replace the issues map, metadata, or previously written operations with values reconstructed from memory. Never generate a fresh file that replaces one with existing content.
If context has been compacted (earlier tool call results are gone, conversation is summarized, or your memory of issues feels vague), recover by reading the operations file from disk:
issues map tells you the complete inventory (every issue key and its verified summary).operations array tells you which issues have been processed (any issue key appearing in at least one operation — including NO_OP — is done).issues map to get the remaining unprocessed issues.NO_OP).NO_OP records ensure every evaluated issue leaves a trace — without them, skipped issues are indistinguishable from unprocessed ones after compaction. Static issueCount (set once in Step 1, never modified) prevents the count from drifting. The issues map persisted in Step 1 means the agent never needs to reconstruct the inventory from memory. Together, these mechanisms make the operations file a self-contained recovery checkpoint.
Query Jira and return a structured issue set. The output stays in the agent session (not written to disk).
jira-project-reference.md for the project key, component, and any label values needed for JQLresolution = Unresolved in the query unless the caller explicitly requests resolved issues. This prevents fetching thousands of closed issues that don't need triage.ORDER BY key ASC to the final JQL used for jira_search (unless that exact ordering is already present)jira_search with the JQL, requested fields, limit=50, and start_at=0total: -1 (unknown count), so do not rely solely on total to decide whether to paginate. After each response, check for a next_page_token in the response or total > start_at + max_results. If either is present, fetch the next page (start_at += 50 or pass page_token) and repeat until no more pages remain. Skipping pagination silently drops issues from the result set.When the caller does not specify fields, use: summary, status, priority, labels, assignee, issuetype, created, updated
description is excluded by default because it can be very large and bloat context across dozens of issues. Analysis skills that need description content should request it explicitly by adding it to the fields list.
All values below must come from jira-project-reference.md. The patterns show the structure; substitute actual values from the reference file. All patterns include resolution = Unresolved by default and end with ORDER BY key ASC.
Placeholder reference:
| Placeholder | Reference section |
|---|---|
{project_key} | Project Constants |
{component} | Project Constants |
{jql_team_id} | Project Constants → Team field (JQL) |
{triage_types} | Issue Types → Standard Query Filters |
{needs_labels_list} | Triage Labels → Needs-* Labels |
{scrum_labels_list} | Scrum Team Labels |
{area_labels_list} | Area Labels |
{triage_labels_list} | Triage Labels → Needs-* Labels |
Untriaged issues:
project = {project_key}
AND component = "{component}"
AND resolution = Unresolved
AND status = New
AND type in ({triage_types})
AND "Epic Link" is EMPTY
AND (Team is EMPTY OR Team = {jql_team_id})
AND (labels is EMPTY OR labels != Security)
AND (labels is EMPTY OR labels not in (Cross-Team))
AND (labels is EMPTY OR labels not in ({scrum_labels_list}))
AND (labels is EMPTY OR labels not in ({needs_labels_list}))
AND Blocked != "True"
ORDER BY key ASC
This is the one query that uses (Team is EMPTY OR Team = ...) because incoming issues may not yet have a team assigned. Issues inside an epic are excluded because they are managed as part of the epic's planned work. Issues with needs-* labels or Blocked = True have already been through triage and identified as waiting on something. They are handled separately by the evaluate-blockers skill. Issues labeled Cross-Team are managed at the program level and excluded from dashboard-specific triage.
JQL negative-label caveat: In Jira,
labels != Xandlabels not in (...)exclude issues with no labels at all — those issues have no label value to compare against, so they silently drop out of results. Every negative label condition must be wrapped in(labels is EMPTY OR ...)to include label-less issues.
Issues missing area labels:
project = {project_key}
AND component = "{component}"
AND resolution = Unresolved
AND type in ({triage_types})
AND "Epic Link" is EMPTY
AND Team = {jql_team_id}
AND status = Backlog
AND (labels is EMPTY OR labels not in ({area_labels_list}))
ORDER BY key ASC
Issues assigned to a scrum team:
project = {project_key}
AND component = "{component}"
AND resolution = Unresolved
AND type in ({triage_types})
AND "Epic Link" is EMPTY
AND Team = {jql_team_id}
AND labels = "{scrum_label}"
ORDER BY key ASC
Stale assigned issues (no update in N days):
project = {project_key}
AND component = "{component}"
AND resolution = Unresolved
AND type in ({triage_types})
AND "Epic Link" is EMPTY
AND Team = {jql_team_id}
AND assignee is not EMPTY
AND updated < -{N}d
ORDER BY key ASC
Issues needing triage attention:
project = {project_key}
AND component = "{component}"
AND resolution = Unresolved
AND type in ({triage_types})
AND "Epic Link" is EMPTY
AND Team = {jql_team_id}
AND labels in ({triage_labels_list})
ORDER BY key ASC
The caller (user or analysis skill) can provide any valid JQL. These patterns are starting points, not exhaustive. All sub-triage queries include the standard filters from jira-project-reference.md § Standard Query Filters. To include resolved issues, the caller must explicitly omit or override the resolution = Unresolved clause. Before execution, normalize every triage query to include ORDER BY key ASC for stable issue ordering.
The JSON format that sits between analysis and execution. Analysis skills produce it; Apply consumes it. The formal JSON Schema is at operations-schema.json. Required operation fields, action enums, and params contracts are defined only in that schema — sub-skills reference it and describe which actions this skill emits, not a duplicate field checklist.
{
"metadata": {
"generated": "2026-03-11",
"query": "project = RHOAIENG AND component = \"AI Core Dashboard\" AND status = New",
"issueCount": 12
},
"issues": {
"RHOAIENG-12345": "Issue title for readability",
"RHOAIENG-12346": "Another issue in the query result"
},
"operations": [
{
"issueKey": "RHOAIENG-12345",
"action": "ADD_LABEL",
"params": { "labels": ["needs-info"] },
"reason": "No reproduction steps, reporter inactive for 30+ days"
},
{
"issueKey": "RHOAIENG-12346",
"action": "NO_OP",
"params": {},
"reason": "Not a dashboard issue — operator-only component with no UI/BFF scope"
}
]
}
issueCount is static. Set once when the file is created (Step 1) to the total number of issues returned by the initial query. Never incremented during processing. This is the authoritative count of the query result set.
issues map contains ALL queried issues. Populated in Step 1 from the verified inventory — every issue returned by the query appears here, regardless of whether it has actionable operations. This serves as the authoritative record of the query result set, as a completeness check (every key in issues must have at least one entry in operations, even if it's NO_OP), and as a deduplication mechanism (avoids repeating summaries on every operation). Per-operation summary is still allowed for single-operation emits or overrides. Apply and dry-run lines use operation.summary ?? issues[issueKey] ?? issueKey.
| Action | MCP Tool | Params |
|---|---|---|
SET_FIELDS | jira_update_issue | { "fields": { ... } } -- passed through as-is |
ADD_LABEL | jira_get_issue + jira_update_issue | { "labels": ["label1"] } -- read-modify-write (see below) |
REMOVE_LABEL | jira_get_issue + jira_update_issue | { "labels": ["label1"] } -- read-modify-write (see below) |
ADD_COMMENT | jira_add_comment | { "comment": "markdown text" } -- visibility enforced at execution time (see below) |
TRANSITION | jira_get_transitions + jira_transition_issue | { "transitionName": "Backlog", "fields": { ... } } -- resolved at execution time; fields is optional |
LINK_DUPLICATE | jira_create_issue_link | { "duplicateOf": "RHOAIENG-99999" } |
LINK_BLOCKED_BY | jira_create_issue_link | { "blockedBy": "RHOAIENG-99999" } |
NO_OP | (none — informational) | {} -- empty params; the reason field explains why no action is needed |
NO_OP is not an executable action — it records that an issue was evaluated and no operations are needed, along with the reason. Every issue from the query result that has no actionable operations must have exactly one NO_OP entry. This ensures the operations file accounts for every issue in the query result (the issues map and operations array are complete and verifiable).
Common reasons for NO_OP:
NO_OP issues still get ai-reviewed: Even when no substantive changes are needed, the ai-reviewed label is added (if not already present) to prevent reprocessing the issue in future triage runs. A NO_OP + ADD_LABEL ai-reviewed pair is the standard output for fully-triaged issues. No triage summary comment is posted for issues whose only operations are NO_OP and ai-reviewed.
Apply skips NO_OP operations during execution. Dry-run shows them for completeness.
All comments posted by triage skills must start with ### AI Triage as the first line. This applies to every ADD_COMMENT operation across all skills -- audit trail comments, description validation requests, area label reviews, and triage summary comments. The header identifies the comment as machine-generated and makes it easy to find/filter in issue history.
All comments posted by triage skills are restricted to Red Hat employees. This is enforced at execution time -- analysis skills do not need to include visibility in their operations. The Apply step (both agent and script paths) automatically applies the visibility from jira-project-reference.md § Comment visibility.
jira_add_comment, always pass visibility: '{"type":"group","value":"Red Hat Employee"}'Operations may include an explicit visibility in their params to override the default. If omitted, the default is applied.
References to people in comments fall into two categories with different syntax:
Action pings (requesting someone do something): Use the Jira mention syntax @[Display Name](accountid:ACCOUNT_ID), which the MCP Markdown-to-ADF converter transforms into a native Jira mention node (clickable, with notifications). Use this only when you are directing a question, request, or call-to-action at that person.
Contextual citations (stating who did something): Use the person's plain display name with no mention syntax. Historical context like "set by Christian Vogt on 2024-11-14" or "comment by Jane Doe (Mar 20)" does not require a notification -- it is providing attribution, not requesting action.
How to get the account ID (for action pings): Call jira_get_user_profile with the person's email address. The response includes an account_id field. The account_id is also available on issue fields (reporter, assignee) and in comment author objects returned by jira_get_issue.
Example -- action ping:
@[Jane Doe](accountid:712020:ce710f80-369e-4b6d-95e2-ca6c9e58d9fb) — can you confirm whether this is still blocked?
Example -- contextual citation (no ping):
Stale `Blocked=True` (~16 months, set by Christian Vogt on 2024-11-14).
Rule of thumb: If removing the reference would lose an actionable ask, it's an action ping. If removing it would only lose historical context, it's a contextual citation.
Every Jira issue key and external resource mentioned in a comment must be a full clickable link. This applies to every occurrence of the key in the comment — not just the first mention. Do not use bare issue keys (RHOAIENG-12345) or shorthand notations (org/repo#123). Although Jira may auto-link issue keys in some rendering contexts, explicit markdown links are more reliable and work across all rendering modes (email notifications, API consumers, ADF export).
| Reference type | Bad (not clickable) | Good (clickable) |
|---|---|---|
| Jira issue | RHOAIENG-12345 | [RHOAIENG-12345](https://redhat.atlassian.net/browse/RHOAIENG-12345) |
| GitHub issue | patternfly-design#1276 | [patternfly/patternfly-design#1276](https://github.com/patternfly/patternfly-design/issues/1276) |
| GitHub PR | odh-dashboard#1234 | [odh-dashboard#1234](https://github.com/opendatahub-io/odh-dashboard/pull/1234) |
| GitHub PR (full) | PR #1234 | [PR #1234](https://github.com/opendatahub-io/odh-dashboard/pull/1234) |
Jira issues: Always use [KEY](https://redhat.atlassian.net/browse/KEY). If the same issue key appears multiple times in a comment (e.g., once in a summary list and again in a ping paragraph), every occurrence must be a link.
GitHub references require the full URL because Jira does not auto-link GitHub shorthand. When referencing a GitHub issue or PR, construct the URL from the org, repo, and number. Use jira-project-reference.md § GitHub Repository for the dashboard repo's owner and name. For external repos (e.g., patternfly/patternfly-design), infer the org and repo from the shorthand or Blocked Reason text.
Only ping the reporter for needs-info -- when the reporter can provide missing details or confirm a decision that only they can make. Two common cases:
Never ping the reporter for other needs-* labels (needs-ux, needs-pm, needs-advisor). These labels signal that a specific role (designer, product manager, tech lead) needs to provide input. The reporter is typically not that person, so pinging them creates noise without producing useful action. These labels are signals to the triage team, not requests to the reporter.
Pass-through to jira_update_issue. The base skill does not need to know which fields are being set -- analysis skills determine their own field names, IDs, and values.
Examples:
{"fields": {"priority": {"name": "Major"}}}{"fields": {"customfield_10001": "ec74d716-af36-4b3c-950f-f79213d08f71-1809"}}{"fields": {"issuetype": {"name": "Bug"}}}The jira_update_issue tool replaces the entire labels array. Calling it with only the new labels would wipe all existing labels. This is destructive and hard to undo across a batch.
Required procedure:
jira_get_issue with fields=labels to get the current labels arrayjira_update_issue with {"labels": [<full resulting array>]}If the current labels already contain the label being added (or don't contain the label being removed), skip the update and mark the operation as success with a note.
Operations use a human-readable transitionName (e.g., "Backlog", "In Progress"). The Apply step resolves it to a numeric transition_id at execution time. An optional fields object can set fields during the transition (e.g., resolution when closing).
jira_get_transitions on the issue to get available transitionstransitionName (case-insensitive)jira_transition_issue with the resolved transition_id. If the operation includes fields, pass them as the fields parameter to jira_transition_issue as a JSON string (e.g., '{"resolution": {"name": "Done"}}').failed with error text that includes the list of available transition names for diagnosisCall jira_create_issue_link with:
link_type: "Duplicate"inward_issue_key: the operation's issueKeyoutward_issue_key: the duplicateOf value from paramsCall jira_create_issue_link with:
link_type: "Blocks"inward_issue_key: the operation's issueKey (the issue that is blocked)outward_issue_key: the blockedBy value from params (the issue that blocks)This creates a link where the blockedBy issue "blocks" the operation's issue (and the operation's issue "is blocked by" the blockedBy issue).
Results from multiple analysis skills can be merged by concatenating their operations arrays. If producers used the root issues map (issue key → summary), merge those objects too (combine maps; on duplicate keys, prefer the later batch or reconcile manually). Apply processes operations in array order regardless of source. A single issue may have multiple operations (e.g., ADD_LABEL + ADD_COMMENT + TRANSITION).
Ordering matters: analysis skills should sequence dependent actions correctly. For example, a comment explaining a close should come before the TRANSITION to Closed.
Conflict detection: during dry-run, if multiple operations target the same issue, flag them for the user to review. This catches cases where one skill adds a label while another transitions to Closed.
Execute operations against Jira. Supports two modes and two sources.
Modes:
Sources:
The agent determines the source from context: "apply operations from .artifacts/triage/ops.json" means from-file; operations flowing from a preceding analysis step means inline.
All operations files are written to: .artifacts/triage/
This path is relative to the workspace root. Create .artifacts/triage/ if it doesn't exist.
operations-YYYY-MM-DD.json using today's date (from the conversation context). If the file already exists, append a numeric suffix: operations-YYYY-MM-DD_(2).json, operations-YYYY-MM-DD_(3).json, etc. Check the directory with ls before choosing the name.There is no separate results file. Execution status is tracked in-place on the operations file — each operation gets a status field as it is executed (see Step 5). This keeps everything in one file, supports selective/partial applies naturally, and avoids proliferating results files.
operations-schema.jsonissueKey looks valid (matches ^[A-Z]+-[0-9]+$)status: "success" are excluded — they are definitively done. Operations with status: "failed" are retried alongside unannotated operations. Report the counts: "N operations to apply (M already succeeded, excluded)."NO_OP operations from execution counts. NO_OP operations are informational records — they are not counted as "operations to apply" and are not executed. They appear in the dry-run summary for completeness but are automatically treated as status: "success" during execution (annotated in-place without any Jira API call).Only unannotated operations (no status field) appear in the summary. Already-applied operations are not shown.
Resolve display text for each line as operation.summary ?? issues[issueKey] ?? issueKey (see operations-schema.json).
Always present the summary grouped by Jira issue key, listing each issue once with all its proposed changes underneath. Every issue key in the summary must be a clickable markdown link to the Jira issue (format: [RHOAIENG-12345](https://redhat.atlassian.net/browse/RHOAIENG-12345)). This makes it easy to review the full picture for each issue at a glance:
Dry-run summary:
[RHOAIENG-12345](https://redhat.atlassian.net/browse/RHOAIENG-12345) -- Widget crashes on save
- Activity Type = Tech Debt & Quality
- Comment to @reporter requesting repro steps
- +`needs-info` -- missing reproduction steps
- +`dashboard-area-model-serving`
- +`ai-reviewed`
[RHOAIENG-12346](https://redhat.atlassian.net/browse/RHOAIENG-12346) -- Add dark mode toggle to cluster settings
- +`needs-ux` -- UI change without mockups
- +`dashboard-area-cluster-settings`
- +`ai-reviewed`
[RHOAIENG-12350](https://redhat.atlassian.net/browse/RHOAIENG-12350) -- Pipeline run table shows wrong status
- **Story -> Bug**, Activity Type = Tech Debt & Quality
- Severity = Moderate, Priority = Normal -> Major (severity floor)
- +`dashboard-area-pipelines`
- +`dashboard-razzmatazz-scrum`
- -> Backlog
- +`ai-reviewed`
[RHOAIENG-12355](https://redhat.atlassian.net/browse/RHOAIENG-12355) -- Waiting on backend API for subscription column
- blocked by [RHOAIENG-12300](https://redhat.atlassian.net/browse/RHOAIENG-12300)
- +`dashboard-area-model-serving`
- +`ai-reviewed`
[RHOAIENG-12370](https://redhat.atlassian.net/browse/RHOAIENG-12370) -- Stale bug already fixed on main
- -> Closed (Done) -- Already fixed on main
- +`ai-reviewed`
[RHOAIENG-12380](https://redhat.atlassian.net/browse/RHOAIENG-12380) -- Operator webhook validation error
- NO_OP: Not a dashboard issue — operator-only component with no UI/BFF scope
NO_OP issues appear at the end of the dry-run summary, separated from actionable issues. They are not included in the executable operation count.
Dry-run mode (default for inline operations):
.artifacts/triage/ (see § Operations File Path for naming).artifacts/triage/operations-2026-03-26.json -- review and apply with: apply operations from that file"Execute mode (default for from-file, or when the user explicitly requests execution):
Continue to Step 4.
Confirmation is all-or-nothing:
The user either approves the entire batch or aborts. There is no per-group or per-issue selection — the dry-run summary (Step 2) is the review checkpoint. If the user wants to exclude specific operations, they edit the operations file before applying.
On approval, process all operations sequentially (one at a time) following the action-specific procedures documented above.
As each operation is executed, annotate it in-place on the operations file. Read the file, update the operation's fields, write it back. This makes the operations file the single source of truth for both what was proposed and what happened.
Status values:
| Status | Meaning |
|---|---|
| (absent) | Not yet attempted — the implicit default for unannotated operations |
success | Completed successfully |
failed | Failed with error (see error field) |
Additional fields set on execution:
| Field | When set | Purpose |
|---|---|---|
status | Always on execution | Tracks outcome |
error | status: "failed" only | Error detail for diagnosis |
commentId | ADD_COMMENT with status: "success" | Jira comment ID, enables later edits |
On failure: log the error, mark the operation failed, and continue to the next operation. Do not abort the batch.
Partial applies: When applying a subset of operations from a file (e.g., "apply operations for RHOAIENG-7530 only"), annotate only the targeted operations. Unannotated operations remain available for future applies.
After all operation groups have been executed, post a summary comment on each issue that had at least one successful, non-bookkeeping operation. This documents what the AI triage changed directly on the issue.
When to post: Execute mode only. Dry-run does not post comments.
Which issues: Any issue with at least one success operation, excluding NO_OP and the ai-reviewed bookkeeping ADD_LABEL. Issues whose only operations are NO_OP + ai-reviewed do not get a triage summary comment — there is nothing to report.
Comment format (Markdown):
The jira_add_comment MCP tool accepts Markdown (not Jira wiki markup) and converts it to ADF. All comment templates below use Markdown syntax.
### AI Triage
| Field | Change |
| --- | --- |
| {field} | {change} |
| | *{reason}* |
| Status | {old status} → {new status} |
Template rules:
### AI Triageai-reviewed), failed operationscustomfield_10001 UUIDs alone in prose; "Moderate" not {"value": "Moderate"}; "Major" not {"name": "Major"}+`label-name` (added) or -`label-name` (removed), using Markdown inline code+`label-a`, +`label-b`+blocked by [`RHOAIENG-12300`](https://redhat.atlassian.net/browse/RHOAIENG-12300) for LINK_BLOCKED_BY and +duplicate of [`RHOAIENG-99999`](https://redhat.atlassian.net/browse/RHOAIENG-99999) for LINK_DUPLICATE. Links appear after Labels rows and before the Scrum Team row.(unset) → Blocked: {reason text} when setting Blocked=True with a reason, or Blocked → (unset) when clearing it. Appears after link rows.(unset) as the explicit "from" value to make reversal unambiguous (e.g., (unset) → RHOAI Dashboard for a newly assigned Team field)| Scrum Team | +`dashboard-monarch-scrum` | with reason sub-row. Normal operation row.| Scrum Team | — (not assigned) | with reason sub-row explaining why (e.g., area has no default team mapping, or areas map to conflicting teams). This is the only comment row that can appear without a corresponding successful operation.{old} → {new} with no reason sub-row| | *{reason text}* | (empty first cell, italicized reason in second cell)Example -- bug with type correction, area label, scrum team, and team assignment:
### AI Triage
| Field | Change |
| --- | --- |
| Type | Story → Bug |
| | *Description reports a crash with stack trace — matches bug criteria, not enhancement.* |
| Activity Type | New Features → Tech Debt & Quality |
| | *Bug classification; Activity aligned to quality/defect work.* |
| Severity | Undefined → Moderate |
| | *Single-user impact with workaround available.* |
| Priority | Normal → Major |
| | *Severity floor: Moderate severity requires at least Major priority.* |
| Labels | +`dashboard-area-pipelines` |
| | *Pipeline-specific error message and file paths in description.* |
| Team | (unset) → RHOAI Dashboard |
| | *Pipeline execution UI errors and file paths in description — dashboard-area-pipelines.* |
| Scrum Team | +`dashboard-razzmatazz-scrum` |
| | *Area 'dashboard-area-pipelines' maps to Razzmatazz.* |
| Status | New → Backlog |
Example -- story with no type change:
### AI Triage
| Field | Change |
| --- | --- |
| Priority | Undefined → Normal |
| | *UX improvement with no urgency signal — default story priority.* |
| Labels | +`enhancement`, +`dashboard-area-model-catalog` |
| | *Standalone enhancement to existing model catalog admin settings.* |
| Scrum Team | +`dashboard-green-scrum` |
| | *Area 'dashboard-area-model-catalog' maps to Green.* |
| Status | New → Backlog |
Example -- issue blocked, not transitioned:
### AI Triage
| Field | Change |
| --- | --- |
| Labels | +`needs-info` |
| | *No reproduction steps or environment details provided.* |
| Labels | +`dashboard-area-model-serving` |
| | *Inference endpoint and serving runtime references in description.* |
| Scrum Team | +`dashboard-zaffre-scrum` |
| | *Area 'dashboard-area-model-serving' maps to Zaffre.* |
Example -- scrum team skipped (unmapped area):
### AI Triage
| Field | Change |
| --- | --- |
| Labels | +`dashboard-area-edge` |
| | *Edge deployment keywords in summary and description.* |
| Scrum Team | — (not assigned) |
| | *Area 'dashboard-area-edge' has no default scrum team mapping.* |
| Status | New → Backlog |
Example -- scrum team skipped (conflicting areas):
### AI Triage
| Field | Change |
| --- | --- |
| Labels | +`dashboard-area-model-registry`, +`dashboard-area-pipelines` |
| | *Model registry and pipeline references in description.* |
| Scrum Team | — (not assigned) |
| | *Areas map to conflicting teams: Green (model-registry), Razzmatazz (pipelines).* |
| Status | New → Backlog |
Example -- issue with blocked-by link and blocked field:
### AI Triage
| Field | Change |
| --- | --- |
| Priority | (unset) → Major |
| | *Backend dependency with customer-facing impact.* |
| Labels | +`dashboard-area-pipelines` |
| | *Pipeline server configuration references in description.* |
| Blocked By | +blocked by [`RHOAIENG-54596`](https://redhat.atlassian.net/browse/RHOAIENG-54596) |
| | *Description states dependency on backend API in [`RHOAIENG-54596`](https://redhat.atlassian.net/browse/RHOAIENG-54596).* |
| Blocked | (unset) → Blocked: Waiting on z-stream 2.25 to ship the backend fix |
| | *External release dependency — cannot proceed until z-stream ships.* |
| Scrum Team | +`dashboard-razzmatazz-scrum` |
| | *Area 'dashboard-area-pipelines' maps to Razzmatazz.* |
Post the comment using jira_add_comment with visibility: '{"type":"group","value":"Red Hat Employee"}' (see § Comment Visibility). This comment is posted after all other operations have been executed for the issue -- it summarizes the final state, not intermediate steps.
After all operations are attempted, report:
Results: 11 succeeded, 2 failed
Failed operations:
[RHOAIENG-12346](https://redhat.atlassian.net/browse/RHOAIENG-12346): TRANSITION -> "Backlog" -- Transition 'Backlog' not available (available: New, In Progress, Closed)
[RHOAIENG-12360](https://redhat.atlassian.net/browse/RHOAIENG-12360): SET_FIELDS priority=Major -- Permission denied
Then provide a JQL query targeting the specific issue keys that had at least one successful operation, along with a clickable Jira link:
key in (RHOAIENG-12345, RHOAIENG-12346, RHOAIENG-12350) ORDER BY key ASC
Build the link as https://redhat.atlassian.net/issues/?jql= followed by the URL-encoded JQL. Encode parentheses as %28 / %29 — unencoded () in the URL breaks markdown link syntax. Present it as a markdown link so the user can click through to verify the changes in the Jira web interface.
For consistency, every issue key shown in Step 7 output must be a clickable markdown link (https://redhat.atlassian.net/browse/<ISSUE-KEY>), including issue-group headers and failed operation lines.
After all operations are attempted and triage summary comments are posted, the operations file already reflects the final state (each operation annotated in-place during Step 5). No separate results file is written.
Verify the file is consistent: every operation that was attempted should have a status field. Report the file path so the user can review.
Example of an annotated operations file after partial execution:
{
"metadata": {
"generated": "2026-03-11",
"query": "...",
"issueCount": 4
},
"issues": {
"RHOAIENG-12345": "Issue title",
"RHOAIENG-12346": "Another issue",
"RHOAIENG-12348": "Operator webhook error",
"RHOAIENG-12350": "Third issue"
},
"operations": [
{
"issueKey": "RHOAIENG-12345",
"action": "ADD_LABEL",
"params": { "labels": ["needs-info"] },
"reason": "No reproduction steps",
"status": "success"
},
{
"issueKey": "RHOAIENG-12345",
"action": "ADD_COMMENT",
"params": { "comment": "### AI Triage\n\n..." },
"reason": "Requesting missing details from reporter",
"status": "success",
"commentId": "16601820"
},
{
"issueKey": "RHOAIENG-12346",
"action": "TRANSITION",
"params": { "transitionName": "Backlog" },
"reason": "Triaged, moving to backlog",
"status": "failed",
"error": "Transition 'Backlog' not available for current status 'In Progress'"
},
{
"issueKey": "RHOAIENG-12348",
"action": "NO_OP",
"params": {},
"reason": "Not a dashboard issue — operator-only component with no UI/BFF scope",
"status": "success"
},
{
"issueKey": "RHOAIENG-12350",
"action": "ADD_LABEL",
"params": { "labels": ["dashboard-area-pipelines"] },
"reason": "Pipeline references in description"
}
]
}
In this example, two operations succeeded, one failed, one is a NO_OP (auto-succeeded), and one has not been attempted yet (no status field). Note that issueCount is 4 (the query result size) and every issue in the issues map has at least one entry in operations.
Re-applying the same operations file automatically resumes where you left off. Step 1 excludes status: "success" operations and retries everything else — failed and unannotated operations are all candidates.
End-to-end orchestration of the analysis pipeline for issues in the New status. Fetches untriaged issues, runs every applicable analysis skill, aggregates operations, and applies them.
Use Full Triage when the user asks to "triage new issues", "run triage", or wants to process the incoming queue. For ad-hoc or selective operations (e.g., "check priority on these 3 bugs"), use individual skills with Fetch/Apply directly.
Transition opt-in: By default, Full Triage does not transition issues from New to Backlog. Issues remain in New so a human can review AI triage results first. To enable transitions, the user must explicitly request them (e.g., "triage and transition", "triage with transitions", "move to backlog", "auto-transition"). See Step 2c for details.
Full Triage always uses this JQL to select its issue set:
project = {project_key}
AND component = "{component}"
AND resolution = Unresolved
AND status = New
AND type in ({triage_types})
AND "Epic Link" is EMPTY
AND (Team is EMPTY OR Team = {jql_team_id})
AND (labels is EMPTY OR labels != Security)
AND (labels is EMPTY OR labels not in (Cross-Team))
AND (labels is EMPTY OR labels not in ({scrum_labels_list}))
AND (labels is EMPTY OR labels not in ({needs_labels_list}))
AND Blocked != "True"
ORDER BY key ASC
This is the standard Untriaged issues query (see Common JQL Patterns). Uses (Team is EMPTY OR Team = ...) because incoming issues may not yet have a team assigned. All values come from jira-project-reference.md. The existing status, label, and blocked filters are sufficient to exclude already-triaged issues — no ai-reviewed guard is needed.
When the user asks to triage a specific scrum team (e.g., "triage Zaffre", "triage Razzmatazz issues"), modify the Target JQL as follows:
AND (labels is EMPTY OR labels not in ({scrum_labels_list}))AND labels = "{scrum_label}"The resulting query:
project = {project_key}
AND component = "{component}"
AND resolution = Unresolved
AND status = New
AND type in ({triage_types})
AND "Epic Link" is EMPTY
AND (Team is EMPTY OR Team = {jql_team_id})
AND (labels is EMPTY OR labels != Security)
AND (labels is EMPTY OR labels not in (Cross-Team))
AND labels = "{scrum_label}"
AND (labels is EMPTY OR labels not in ({needs_labels_list}))
AND Blocked != "True"
ORDER BY key ASC
All other query filters remain unchanged. The rest of the pipeline runs normally — the assign-scrum-team skill (Step 2b, skill 6) will naturally skip these issues since they already have a scrum label.
Full Triage is designed to minimize context window usage so it can process large issue queues without running out of context. Four mechanisms work together:
jira_get_issue call at the start of each iteration.This means context at any point holds roughly: one issue's full data + one sub-skill's reference material + the triage infrastructure. This is bounded and constant regardless of queue size.
Sub-agent delegation gives each issue a completely fresh context window, allowing deeper analysis without accumulated tool call history from previous issues.
Sub-agent delegation is opt-in only. The default is always inline processing — no sub-agents. Use sub-agents only when the user explicitly requests it:
When the user does not request delegation, process all issues inline using the per-issue pattern in Steps 2.0–2e regardless of queue size. Never enable sub-agents without user confirmation. Never suggest sub-agents proactively.
When delegating, the parent agent becomes a lightweight coordinator:
The coordinator does not read sub-skill files, fetch issue details, or run the analysis pipeline. It holds only the verified inventory (issue keys + summaries) and the operations file path.
Each sub-agent receives a self-contained prompt — sub-agents have no access to the parent conversation. Use the Task tool with subagent_type: "generalPurpose". Do not set readonly: true (sub-agents need MCP access for Jira API calls and write access for the operations file).
Template (substitute {placeholders} from the inventory):
Process one Jira issue through the Full Triage analysis pipeline.
**Issue:** {issueKey}
**Operations file:** {operations_file_path}
**Position:** [{current}/{total}]
**Transitions enabled:** {yes|no}
**Steps:**
1. Read these files in order:
- `.claude/skills/jira-triage/persona.md` — execution protocol, follow strictly
- `.claude/skills/jira-triage/jira-project-reference.md` — all Jira field IDs, labels, values
- `.claude/skills/jira-triage/SKILL.md` — read Capability 3, Steps 2.0 through 2e
2. Execute Steps 2.0–2e for {issueKey}:
- 2.0: Fetch full details via jira_get_issue (use the detail fields listed in § Fields)
- 2.0a: Template gate — if summary/labels/description indicate a template issue, write NO_OP + ai-reviewed and return
- 2a: Team gate — evaluate dashboard ownership; if not owned, write NO_OP with reason
- 2b: Analysis pipeline — read each sub-skill SKILL.md and run in documented order:
validate-issue-type → validate-description → validate-priority-severity →
evaluate-blockers (Tag) → validate-area-label (Tag) → assign-scrum-team
- 2c: Status transition — ONLY if "Transitions enabled = yes": TRANSITION to Backlog if no blocking signals. If "Transitions enabled = no", skip this step entirely.
- 2d: Finalize — ADD_LABEL ai-reviewed
- 2e: Write operations to {operations_file_path}
3. Return a one-line progress summary in this format:
"{issueKey} -- {summary}: {result}"
Examples:
"RHOAIENG-12345 -- Widget crashes: type OK, severity=Moderate, priority=Major, area=model-serving, scrum=Zaffre → Backlog"
"RHOAIENG-12345 -- Widget crashes: type OK, severity=Moderate, priority=Major, area=model-serving, scrum=Zaffre"
"RHOAIENG-12346 -- Operator webhook error: skipped (not a dashboard issue)"
If the issue was skipped at the team gate, return:
"RHOAIENG-12346 -- Operator webhook error: skipped ({reason})"
The sub-agent reads the full SKILL.md and follows Steps 2.0–2e as documented — the prompt does not duplicate the step logic, it references it. This ensures the sub-agent always follows the current version of the pipeline.
Dispatch one sub-agent at a time in key-ascending order. After each completes:
[3/9] {progress_line}If a sub-agent fails (returns an error, times out, or produces no file update):
[3/9] RHOAIENG-12345 -- FAILED: {error summary}Full Triage uses two field sets to manage context efficiently:
List fields (Step 1 — bulk fetch for the issue queue):
summary, status, priority, labels, assignee, issuetype, created, updated, customfield_10001, components
This is a lightweight set for building the issue queue. It includes Team (customfield_10001) and Components for the Step 2a team gate (which can skip issues without reading their description). It excludes description and other large fields to keep the bulk fetch small.
Detail fields (Step 2 — per-issue fetch via jira_get_issue):
summary, status, priority, labels, assignee, issuetype, created, updated, description, reporter, comment, customfield_10840, customfield_10517, customfield_10483, customfield_10001, customfield_10464, issuelinks, components
Use comment_limit=20 on the jira_get_issue call to cap comment volume. The comment field must be explicitly included in the fields parameter for the API to return comments.
This is the full field set required by all analysis skills. Fetched once per issue at the start of processing, used for the duration of that issue's pipeline, then no longer needed.
_(N) if needed). Create the file with the complete initial skeleton — issueCount and issues are populated now, not incrementally:
{
"metadata": {
"generated": "<today's date>",
"query": "<the JQL used>",
"issueCount": <total issues from verified inventory>
},
"issues": {
"<every issue key>": "<summary from inventory>",
...
},
"operations": []
}
issueCount is set to the total number of issues in the verified inventory and never changes after this point. The issues map contains every issue from the query result with its summary. This persists the authoritative inventory to disk before any processing begins — if context compacts, the file is the source of truth.Delegation check: If the user opted into sub-agent delegation ("think deep"), use the sequential dispatch pattern from § Sub-Agent Delegation. The coordinator dispatches one sub-agent per issue using the prompt template (setting "Transitions enabled" to yes/no based on whether the user opted in); each sub-agent follows Steps 2.0–2e below autonomously. The rest of this section documents what happens per issue — whether executed inline or by a delegated sub-agent.
Process issues one at a time in key-ascending order. Complete all sub-steps for a single issue before moving to the next, then write that issue's operations to the file on disk before proceeding. This bounds context usage: only one issue's full details are in working memory at any point, and completed issues are persisted to disk rather than accumulated in session.
For each issue in the fetched set:
Step 2.0: Fetch issue details. Call jira_get_issue with the detail fields for this single issue. This loads the description, reporter, severity, blocked state, issue links, and other fields needed by the analysis pipeline. Only this one issue's details are in context at a time.
Then perform sub-steps 2a through 2e:
Template issues are recurring task templates (e.g., "TEMPLATE - Learning Activities", "Nightly build analysis - TEMPLATE") that serve as blueprints for cloning actual work items. They should not be triaged, transitioned, or modified by the pipeline. See jira-project-reference.md § Template Issues for detection signals.
Detection signals (any one is sufficient):
template)[replace this], TODO:, repeating placeholder sections)If detected: Produce a single NO_OP with reason citing the template signal. Skip Steps 2a–2d entirely — do not assign Team, run the analysis pipeline, transition, or add ai-reviewed. Templates are not work items and should not be touched at all. Proceed directly to Step 2e (write to disk).
{
"issueKey": "RHOAIENG-12345",
"action": "NO_OP",
"params": {},
"reason": "Template issue — summary contains 'TEMPLATE'; not a real work item"
}
This is the entry gate. Full Triage only processes issues that belong to the RHOAI Dashboard program team. The component = "AI Core Dashboard" JQL filter only ensures that component appears on the issue; it does not mean the dashboard team owns the work (issues may have multiple components for relationship tracking). This step evaluates Jira Components, labels, and description together.
Hard rule — no triage for other teams: If you conclude the issue is owned by or belongs to another program team (not RHOAI Dashboard), stop for this issue immediately. Do not run Step 2b (analysis pipeline) or Step 2c–2d (transition and finalize). Do not emit operations meant to dashboard-triage a non-dashboard issue (including type, priority, area, scrum, blockers, or comments). Report the issue as skipped with a one-line reason. The Dashboard triage skills are not a substitute for the owning team's intake.
Read first: jira-project-reference.md § When not to treat an issue as dashboard-owned (team gate) for multi-component handling, labels outside dashboard-area-*, and descriptions dominated by backend/API/automation vs dashboard UI.
customfield_10001) is set to any value other than RHOAI Dashboard (see jira-project-reference.md § Project Constants), skip this issue -- the issue is already attributed elsewhere; do not override Team to pull it into dashboard triage. Produce a NO_OP with reason (e.g., "Team already set to {team name} — not dashboard-owned"). (This case is rare when using the default Full Triage JQL, but can appear if the issue set came from a custom query.)ADD_LABEL Cross-Team or NO_OP.
AI Core Dashboard; anddashboard-area-*); infer from wording — do not rely on a hardcoded list of product names; andopendatahub-io/odh-dashboard repo/BFF scope.dashboard-area-* (see jira-project-reference.md § Content Signals). This is the strongest signal -- if you can identify which area of the dashboard is affected, it belongs to the dashboard team.{
"issueKey": "RHOAIENG-12345",
"summary": "Issue title",
"action": "SET_FIELDS",
"params": { "fields": { "customfield_10001": "ec74d716-af36-4b3c-950f-f79213d08f71-1809" } },
"reason": "<one-sentence citing the positive signal, e.g., 'Model serving deployment wizard UI — dashboard-area-model-serving'>"
}
ADD_LABEL Cross-Team or NO_OP. Proceed to Step 2e to write the operation to disk. Move to the next issue.Skip outcome — Cross-Team label vs NO_OP:
When the team gate determines the issue is not dashboard-owned (items 2 or 6), the output depends on whether the AI Core Dashboard component is legitimately on the issue:
Component is legitimate → ADD_COMMENT + ADD_LABEL Cross-Team: The other component corresponds to a service the dashboard consumes through a package or BFF in this repo (e.g., AI Evaluations → packages/eval-hub/, Model Registry → packages/model-registry/). The AI Core Dashboard component correctly reflects the dashboard's integration interest — it should stay. Apply Cross-Team to acknowledge cross-component coordination and exclude the issue from future dashboard triage queries. The label signals that the work belongs to the other component's team while the dashboard has a stake.
Produce two operations (comment before label per canonical ordering):
ADD_COMMENT explaining the routing decision — why the issue is not dashboard-owned, which component/team owns the work, and why the AI Core Dashboard component is legitimate (the dashboard package/BFF that consumes the service). This is the only change triage makes to the issue, so the comment is the sole record of the decision. Follow the ### AI Triage header and comment visibility rules.ADD_LABEL Cross-Team to exclude the issue from future dashboard triage queries.{
"issueKey": "RHOAIENG-12345",
"action": "ADD_COMMENT",
"params": { "comment": "### AI Triage\n\nThis issue is owned by the **{owning component}** team. The AI Core Dashboard component is valid — the dashboard consumes this service through `{dashboard package/BFF path}` — but the described work ({brief description of why it's not dashboard scope}) does not involve the dashboard UI or BFF.\n\nLabeled `Cross-Team` to reflect the cross-component relationship." },
"reason": "<one-sentence summary of the routing decision>"
},
{
"issueKey": "RHOAIENG-12345",
"action": "ADD_LABEL",
"params": { "labels": ["Cross-Team"] },
"reason": "<cite the dashboard package/BFF that consumes the service, why the component is valid, and which other component owns the work>"
}
Component is incorrect or incidental → NO_OP: The dashboard has no package, BFF, or integration relationship with the other component. The AI Core Dashboard component is likely a mis-filing or cross-reference. Produce a NO_OP with reason (e.g., "Not a dashboard issue — operator-only component with no UI/BFF scope").
See jira-project-reference.md § Project Constants for the Team field ID and value.
Run the analysis skills in order only for issues that passed Step 2a. Each skill reads the issue data already in session and produces operations. Read each skill's SKILL.md before executing it.
Workspace tools are available during analysis. Some analysis skills (notably validate-area-label and assign-scrum-team) may use workspace tools like Glob to resolve file names mentioned in issues to their full repository paths. This is expected — the codebase is a signal source alongside Jira data, and is especially important for test failure/flake issues where the spec file name is the primary routing signal.
If during Step 2b you determine the issue belongs to another team (not RHOAI Dashboard), stop the pipeline for this issue: do not run remaining analysis skills, do not run Step 2c or 2d, and discard any operations already drafted for it in this run. Produce a single NO_OP with reason (e.g., "Late team-gate: not a dashboard issue — {reason}") and write it to the file in Step 2e. Step 2a should prevent most cases; this catches late realizations.
Pipeline order:
| Order | Skill | Path | Why this order |
|---|---|---|---|
| 1 | validate-issue-type | ../jira-validate-issue-type/SKILL.md | May change issue type and Activity Type (customfield_10464) together. Downstream skills should use the proposed type. Cypress/CI flake Tasks are not promoted to Bug (see jira-project-reference.md § Issue Type Classification Criteria) |
| 2 | validate-description | ../jira-validate-description/SKILL.md | Checks description completeness. May add needs-info. Runs before priority/severity because a missing description limits severity assessment confidence |
| 3 | validate-priority-severity | ../jira-validate-priority-severity/SKILL.md | Bugs only. Assesses severity from description content and enforces the severity-to-priority floor |
| 4 | evaluate-blockers (Tag mode) | ../jira-evaluate-blockers/SKILL.md | Checks for blocking conditions, needs-* labels, and dependency links. Runs after earlier skills that may have changed the type or flagged missing info |
| 5 | validate-area-label (Tag mode) | ../jira-validate-area-label/SKILL.md | Assigns dashboard-area-* labels based on multi-signal analysis. Runs after blockers because it benefits from the corrected issue type and enriched context from earlier skills. Area labels are independent of blocking state. |
| 6 | assign-scrum-team | ../jira-assign-scrum-team/SKILL.md | Maps area labels to a scrum team label. Runs after area labels are assigned so it can use proposed labels as input. |
Important: When validate-issue-type produces a type change for an issue (e.g., Story → Bug), downstream skills must use the proposed type, not the original. For example, if an issue is reclassified from Story to Bug, validate-priority-severity should evaluate it as a bug even though the type hasn't been changed in Jira yet. The same applies to Activity Type when emitted in the same SET_FIELDS batch — treat the proposed Activity value as the intended program classification after Apply.
By default, Full Triage does NOT transition issues from New to Backlog. Issues remain in New after triage so a human can review the AI triage results before moving them to the backlog. This step is skipped entirely unless the user explicitly opts in.
Opt-in triggers (user must include one of these phrases):
When the user does not opt in, skip this step — produce no TRANSITION operations. All other triage steps (type validation, description, priority/severity, blockers, area labels, scrum team, ai-reviewed) still run normally.
When opted in, decide whether this issue should be transitioned from New to Backlog or left in New.
Transition to Backlog if the issue has none of the following blocking signals in its accumulated operations:
ADD_LABEL with any needs-* label (needs-info, needs-ux, needs-pm, needs-advisor)LINK_BLOCKED_BY (dependency on another issue)SET_FIELDS setting the Blocked field to True (customfield_10517)ADD_LABEL with feature-request (requires RFE/epic handling before backlog)Leave in New if any of the above are present. The issue needs input or resolution before it is ready for the backlog.
If the issue qualifies, produce a TRANSITION operation:
{
"issueKey": "RHOAIENG-12345",
"summary": "Issue title",
"action": "TRANSITION",
"params": { "transitionName": "Backlog" },
"reason": "Triage complete, no blocking conditions — ready for backlog"
}
Add the ai-reviewed label as the final operation for this issue. This is a passive marker for human reference and reporting — no skill, query, or JQL filter depends on this label.
{
"issueKey": "RHOAIENG-12345",
"summary": "Issue title",
"action": "ADD_LABEL",
"params": { "labels": ["ai-reviewed"] },
"reason": "Marking as processed by AI triage pipeline"
}
This label is added to every issue in the query result — regardless of what other operations were produced, whether the issue was transitioned, or whether the issue was skipped at the team gate — except template issues (detected at Step 2.0a), which receive no operations at all. For non-template NO_OP issues, ai-reviewed prevents reprocessing in future triage runs. If the label is already present, skip the operation.
Per-issue operation order: The complete set of operations for this issue is ordered as:
NO_OP if skipped at gate / no changes neededai-reviewed (this step — always, unless already present)After completing all sub-steps for this issue, immediately persist its operations to the file on disk:
operations array (including NO_OP for skipped issues)The issues map and metadata.issueCount are already populated from Step 1 — do not modify them here.
Every issue must produce at least one operation. If the issue passed the full pipeline, it will have analysis operations + ai-reviewed + possibly a TRANSITION (only if the user opted in to transitions). If the analysis produced no changes (everything already correct), it will have NO_OP + ai-reviewed. If the issue was skipped at the team gate (Step 2a) or during analysis (Step 2b late realization), it will have NO_OP + ai-reviewed. If the issue was detected as a template (Step 2.0a), it will have only NO_OP — no ai-reviewed, no other operations. This ensures the operations file accounts for every issue in the query result.
Then report a one-line progress summary to the user:
[3/9] RHOAIENG-12345 -- Widget crashes on save: type OK, severity=Moderate, priority=Major, area=model-serving, scrum=Zaffre
or when transitions are opted in and the issue qualifies:
[3/9] RHOAIENG-12345 -- Widget crashes on save: type OK, severity=Moderate, priority=Major, area=model-serving, scrum=Zaffre → Backlog
or for skipped issues:
[4/9] RHOAIENG-12346 -- Operator webhook validation error: skipped (not a dashboard issue)
After writing, move to the next issue. The completed issue's description and detailed field data are no longer needed in working memory.
The operations file was built incrementally during Step 2 — it is already on disk with all issues' operations and the root issues map. No assembly step is needed.
Pass the file path to the Apply capability. Apply defaults to dry-run mode: it reads the file, validates, presents the summary, and stops. The user reviews the output and then explicitly applies from file when ready.
If the user requested immediate execution (e.g., "triage and apply"), Apply runs in execute mode instead: read file, validate, summarize, confirm, execute, and annotate results in-place.
The dry-run summary groups all operations by issue key. The user reviews the full picture and then approves or aborts the entire batch.
Default (no transitions):
User: "Triage new issues"
1. Fetch (lightweight) + Initialize:
9 issues in New status. Operations file created at
.artifacts/triage/operations-2026-03-26.json
"Found 9 untriaged issues. Processing one at a time..."
2. Process each issue (fetch details → analyze → write to disk):
[1/9] RHOAIENG-12340 -- Widget crashes on save
2.0 Fetch full details (description, reporter, etc.)
2.0a Template gate: not a template → continue
2a. Team gate: Team empty → content confirms dashboard → SET_FIELDS team
2b. Pipeline: type OK → description OK → severity=Moderate, priority=Major
→ no blockers → area=model-serving → scrum=Zaffre
2c. Skipped (transitions not requested)
2d. ADD_LABEL ai-reviewed
2e. Write 5 operations to file
→ "type OK, severity=Moderate, priority=Major, area=model-serving, scrum=Zaffre"
[2/9] RHOAIENG-12341 -- Pipeline run table wrong status
2.0 Fetch full details
2a. Team gate: Team empty → content confirms dashboard → SET_FIELDS team
2b. Pipeline: type Story→Bug → description incomplete (ADD_COMMENT + needs-info)
→ skip severity (needs-info) → needs-info blocker → area=pipelines → scrum=Razzmatazz
2c. Skipped (transitions not requested)
2d. ADD_LABEL ai-reviewed
2e. Write 7 operations to file
→ "Story→Bug, needs-info, area=pipelines, scrum=Razzmatazz (blocked)"
... (6 more issues processed the same way) ...
[9/9] RHOAIENG-12348 -- Operator webhook validation error
2.0 Fetch full details
2a. Team gate: no dashboard evidence → NO_OP
2e. Write NO_OP to file
→ "skipped (not a dashboard issue)"
Summary: 8 processed, 1 skipped (NO_OP)
Operations file: .artifacts/triage/operations-2026-03-26.json
3. Apply (dry-run):
Read operations file, validate, present summary:
RHOAIENG-12340 -- Widget crashes on save
- Team = RHOAI Dashboard
- Severity = Moderate, Priority = Major
- +`dashboard-area-model-serving`, +`dashboard-zaffre-scrum`
- +`ai-reviewed`
RHOAIENG-12341 -- Pipeline run table wrong status
- Team = RHOAI Dashboard
- **Story → Bug**, Activity Type = Tech Debt & Quality
- Comment to @reporter requesting repro steps
- +`needs-info`, +`dashboard-area-pipelines`, +`dashboard-razzmatazz-scrum`
- +`ai-reviewed`
...
RHOAIENG-12348 -- Operator webhook validation error
- NO_OP: Not a dashboard issue — operator-only component with no UI/BFF scope
"Review and apply with: apply operations from
.artifacts/triage/operations-2026-03-26.json"
User: "Apply operations from .artifacts/triage/operations-2026-03-26.json"
→ Validate, summarize, confirm, execute, post triage comments, report results
With transitions (opt-in):
User: "Triage new issues and transition to backlog"
(same as above, but Step 2c is active)
[1/9] RHOAIENG-12340 -- Widget crashes on save
...
2c. No blocking signals → TRANSITION to Backlog
...
→ "type OK, severity=Moderate, priority=Major, area=model-serving, scrum=Zaffre → Backlog"
[2/9] RHOAIENG-12341 -- Pipeline run table wrong status
...
2c. Has needs-info → remain in New
...
→ "Story→Bug, needs-info, area=pipelines, scrum=Razzmatazz (blocked)"
.claude/rules/jira-creation.md for creating new issues| File | Purpose |
|---|---|
persona.md | Execution protocol: thoroughness, verification, no assumptions -- applies to all triage skills |
jira-project-reference.md | All Jira-specific values (project constants, field IDs, labels, mappings) |
operations-schema.json | Formal JSON Schema for the operations format |