with one click
release-health-analysis
Analyze release health data and produce assessment
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Analyze release health data and produce assessment
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Surface your daily task list — QA-ready tickets, sprint backlog, carry-over items, open PRs, RHEL verification queue, and quarterly reminders. Use at the start of your day to see what needs attention.
Show detailed info about a specific TODO task — its tracked links, notes, and live Jira/PR status. Use when the user asks about a task, wants context to resume work on it, or asks what's needed to finish a ticket. Not for listing all tasks (read the TODO file directly or use edge-ic:sprint-status for sprint-wide views)
End-of-week automation - review active work and prepare next week's TODO
Run the full Prow CI release testing workflow — create PR, trigger jobs, check status, merge PR, download and upload artifacts
Generate interactive PCP performance dashboard from a Prow job URL
Download Prow job artifacts, identify root cause of failure, and produce a structured error report
| name | release-health-analysis |
| description | Analyze release health data and produce assessment |
| allowed-tools | Read, Write, Bash, mcp__plugin_mcp-atlassian_mcp-atlassian__jira_search |
| user-invocable | false |
Perform the full hierarchy analysis, risk assessment, and generate all report sections, writing the results to analysis.md in the work directory.
The parent release-health skill spawns this agent during Phase 4, after Phase 3 (Epic Fetcher + Spike Finder) completes. This is the only Phase 4 agent.
jira_search)Read tool (all four phase data files + Edge Scrum Laws + .roster.json)Write toolThis agent does not modify any Jira data.
Substituted by the parent before spawning:
| Placeholder | Description |
|---|---|
{WORKDIR} | Work directory path |
{VERSION} | OCP release version (e.g., 4.19, 5.0) |
{TODAY} | Today's date in YYYY-MM-DD format |
{REFINEMENT_SPRINT_NUM} | Sprint number of the refinement sprint |
{REFINEMENT_MODE} | true or false — when true, produce additional REFINEMENT_BY_SME output section |
plugins/edge-scrum/.roster.json
Extract: members array — use username for ownership matching, sp_target per member for capacity.
Derive roster_size (count of members) and total_sp_per_sprint (sum of all sp_target values).
If a member is missing sp_target, default that member to 8. If the file is absent, halt with an error.plugins/edge-scrum/references/laws/:
07-workflow-states.md — done states per issue type02-jira-stories.md — bugs-always-zero-SP rule and story pointing04-jira-epics.md — epic sizing scales05-jira-features.md — feature/initiative sizing scales01-jira-projects.md — OCPBUGS components06-jira-fields.md — custom field IDs{WORKDIR}/sprints.json{WORKDIR}/features.json{WORKDIR}/epics.json{WORKDIR}/spikes.jsonSplit epic_keys_csv from epics.json into batches of ~20. For each batch, paginate with page_token, limit=50 until all results are fetched:
project in (OCPEDGE, USHIFT, OCPBUGS) AND "Epic Link" in ({batch_csv}) ORDER BY priority ASC
Also fetch unlinked OCPBUGS bugs (use components from Laws). Paginate with page_token, limit=50 until all results are fetched:
project = OCPBUGS
AND component in ("Installer / Single Node OpenShift", "Two Node with Arbiter", "Two Node Fencing", "Logical Volume Manager Storage")
AND fixVersion in ("{VERSION}", "{VERSION}.0")
AND "Epic Link" is EMPTY
ORDER BY priority ASC
Fields per issue:
key, summary, status, issuetype, priority, assignee, sprint, labels, updated,
customfield_10028, customfield_10470, customfield_10021, customfield_10014, issuelinks
Per issue, compute:
sp: customfield_10028 — ALWAYS 0 for any Bug issuetype, no exceptionsepic_key: customfield_10014 (or "No Epic")flagged: customfield_10021 is non-emptyblocked_by: issuelinks where type.inward = "is blocked by" AND blocker status not in {Closed, Verified, Done}stale: status in {In Progress, Review} AND updated older than 5 business days from {TODAY}Group: Feature → Epics → Issues.
Orphan groups: (No Feature), (No Epic), (Unlinked Bugs).
Done states (from Laws):
Per-Epic rollup:
| Field | Calculation |
|---|---|
total_issues | count of all child issues |
done_issues | issues in done state |
total_sp | sum of SP (bugs always 0) |
done_sp | SP of done issues |
remaining_sp | SP of non-done issues |
completion_pct | done_sp / total_sp × 100; fallback: done_issues / total_issues × 100 if total_sp = 0 |
unpointed_count | non-Bug issues with SP null or 0 |
blocked_count | flagged OR blocked_by non-empty OR "Blocked" in labels |
unassigned_count | no assignee |
Per-Feature rollup: aggregate epics; add epic_count and done_epics.
Release totals:
total_remaining_sp = sum of all epics' remaining_spmax_sp_capacity = remaining_sprint_count (from sprints.json) × total_sp_per_sprint (from .roster.json)After building the hierarchy, reassess epics_appear_refined for features that the transform did not already mark as refined. For each feature where spike_missing = true AND spike_on_epic = false AND epics_appear_refined = false AND feature status is not "New" or "Refinement":
description (from epics.json) and examine the child stories/tasks created under it (from the hierarchy)epics_appear_refined = true for that featureUpdate features_refined_via_epics count accordingly.
Read refinement_sprint_closed from sprints.json.
7a — Schedule Risk — Skip entirely if refinement_sprint_closed = false.
If refinement_sprint_closed = true, per Feature:
| Condition | Status |
|---|---|
| status ∈ {Done, Closed} | ✅ Complete |
| 0% complete AND ≤ 2 dev sprints remaining | 🔴 Critical |
| 0% complete AND ≤ 4 dev sprints remaining | 🟡 At Risk |
| completion_pct ≥ expected_dev_pct | 🟢 On Track |
| completion_pct ≥ expected_dev_pct × 0.75 | 🟡 Slightly Behind |
| completion_pct < expected_dev_pct × 0.75 | 🔴 At Risk |
| no Epics AND ≥ 1 dev sprint remaining | 🔴 Unplanned |
7b — Staffing:
sme = "None" → 🔴 "No SME assigned" — Action: "Assign SME this sprint"qa_contact = "None" AND status ≠ Closed → 🟡 "No QA contact"docs_approver = "None" → 🟢 "No docs approver"assignee = "Unassigned" → 🟡 "No epic DRI"qa_contact = "None" AND status ≠ Closed → 🟡 "No epic QA contact"7c — Refinement — Spike Rules (based on refinement_sprint_closed):
Evaluate in this order — first match wins:
spike_status = "Closed" → ✅ "Closed" — direct spike completed, no riskspike_missing AND (spike_on_epic = true OR epics_appear_refined = true) → ✅ "Via epics" — no spike riskrefinement_sprint_closed = false:
spike_missing → 🔴 "No refinement spike — SME must create one in Sprint {REFINEMENT_SPRINT_NUM} that blocks this Feature"spike_in_ref_sprint → 🟡 "Spike not in refinement sprint — move to Sprint {REFINEMENT_SPRINT_NUM}"spike_in_ref_sprint AND status ≠ Closed → 🟢 "Spike in progress (expected)"refinement_sprint_closed = true:
spike_overdue = true → 🔴 "Refinement spike not closed — refinement incomplete"spike_missing → 🔴 "No refinement spike found; delivery epics may be under-refined"7c — Refinement — General:
has_ac = false → 🟡 "Missing acceptance criteria"size = "Unsized" → 🟡 "Feature not sized"epic_count = 0 → 🔴 "No epics — unplanned"has_ac = false → 🟡 "Epic missing AC"size = "Unsized" → 🟡 "Epic not sized"total_issues = 0 → 🟡 "Empty epic — no stories"sp null or 0 → 🟡 "Needs estimation"assignee = "Unassigned" → 🟡 "Needs owner"7d — Blocked/Stalled:
flagged = true → 🔴 "Impediment flagged"blocked_by non-empty → 🔴 "Blocked by {keys}""Blocked" in labels → 🔴 "Labeled Blocked""Parked" in labels → 🟡 "Parked"stale = true → 🟡 "Stale — no update in 5+ business days"7e — Release-Level:
remaining_sprint_count ≤ 2 → 🔴 "XL-sized epic unlikely to complete"remaining_sprint_count ≤ 1 → 🔴 "L-sized epic at risk"total_remaining_sp > max_sp_capacity → 🔴 "Capacity risk"refinement_sprint_closed AND total_features > 0) (features_missing_spike - features_refined_via_epics) / total_features > 0.5 → 🔴 "Systematic refinement gap"refinement_sprint_closed AND total_features > 0) (features_with_closed_spike + features_refined_via_epics) / total_features < 0.75 → 🟡 "Refinement coverage below 75%"Write to {WORKDIR}/analysis.md with this exact sentinel structure:
===ANALYSIS_META===
{
"total_features": <int>,
"features_on_track": <int>,
"features_at_risk": <int>,
"features_complete": <int>,
"high_risk_count": <int>,
"medium_risk_count": <int>,
"low_risk_count": <int>,
"overall_health": "Critical|At Risk|On Track|Complete",
"actual_completion_pct": <float>,
"expected_dev_completion_pct": <float>,
"features_with_spike": <int>,
"features_with_closed_spike": <int>,
"features_missing_spike": <int>,
"features_spike_on_epic": <int>,
"features_refined_via_epics": <int>,
"remaining_sp": <int>,
"max_sp_capacity": <int>,
"capacity_risk": <bool>,
"top_risks": ["<concise description>", ...],
"sprint_recommendation": "<one sentence priority for this sprint>",
"refined_count": <int>,
"needs_attention_count": <int>
}
===END_META===
When `{REFINEMENT_MODE}` = `true`, compute and include `refined_count` (features with zero gaps at all levels) and `needs_attention_count` (`total_features - refined_count`). When `false`, set both to 0.
===SECTION:DASHBOARD===
| Key | Feature/Initiative | Type | Status | SME | QA | Size | Refn Spike | Epics Done | Progress | Risk |
|---|---|---|---|---|---|---|---|---|---|---|
(one row per feature, sorted by Jira rank — preserve the priority order returned by the JQL query)
Spike column: ✅ Closed | ✅ Via epics | 🔄 In Progress | ⚠️ Missing | ⏳ Open
===END_SECTION===
===SECTION:FEATURE_DETAIL===
(one subsection per feature, sorted by Jira rank — same order as the dashboard)
### OCPSTRAT-XXX: {Summary}
**Type**: {type} | **Status**: {status} | **Health**: {emoji}
**SME**: {sme} | **QA**: {qa_contact} | **Docs**: {docs_approver}
**Refinement Spike**: {spike_key — ✅ Closed | ✅ Via epics | 🔄 In Progress | ⚠️ Missing | ⏳ Open}
**Progress**: {done_sp}/{total_sp} SP ({pct}%) — {done_epics}/{epic_count} epics complete
**Refinement**: {✅ Has AC | ⚠️ Needs AC} | **Sized**: {✅ | ⚠️ Unsized}
#### Epics
| Epic | DRI | QA | Size | Issues | Progress | Status | Health |
#### Risks & Gaps
- {severity}: {description} — Action: {action}
#### Actions
- [ ] {action} — Owner: {owner}
===END_SECTION===
===SECTION:EPIC_DETAIL===
(only epics with blocked, stale, flagged, or unpointed issues)
### OCPEDGE-XXX: {Epic Summary}
**DRI**: {assignee} | **QA**: {qa_contact} | **Progress**: {done}/{total} SP
| Issue | Type | DRI | SP | Status | Sprint | Flags |
|---|---|---|---|---|---|---|
Flag legend: 🚫 Blocked | ⏸️ Parked | ⚠️ Unpointed | 🔴 Flagged | 💤 Stale
===END_SECTION===
===SECTION:RISK_REGISTER===
### 🔴 High Priority
| # | Issue | Risk Type | Description | Owner | Action |
|---|---|---|---|---|---|
### 🟡 Medium Priority
| # | Issue | Risk Type | Description | Owner | Action |
|---|---|---|---|---|---|
### 🟢 Low / Informational
| # | Issue | Risk Type | Description | Owner | Action |
|---|---|---|---|---|---|
===END_SECTION===
===SECTION:REFINEMENT_BACKLOG===
### Features/Initiatives
- OCPSTRAT-XXX — Missing: {items}
### Epics
- OCPEDGE-XXX — Missing: {items}
### Stories/Tasks (Unpointed or Unassigned)
- OCPEDGE-XXX (Epic: OCPEDGE-YYY) — {issue}
===END_SECTION===
===SECTION:SPRINT_FORECAST===
| Sprint | State | Projected SP | Cumulative | Cumulative % | Notes |
|---|---|---|---|---|---|
Use `total_sp_per_sprint` (from `.roster.json`) as the default velocity if no completed dev sprints exist.
Flag if projected completion at branch cut < 85% → add ⚠️ in Notes.
===END_SECTION===
===SECTION:ACTIONS===
### This Sprint — Immediate
1. {Action} — Owner: {person}
### Next Sprint
1. {Action} — Owner: {person}
### Grooming / Planning
1. {Action} — Target: Sprint {N} grooming
===END_SECTION===
{REFINEMENT_MODE} = true)After writing all standard sections above, append this additional section. It reorganizes the refinement gaps from the existing analysis into an SME-centric view with natural language summaries.
===SECTION:REFINEMENT_BY_SME===
## Refinement Status: OCP {VERSION}
**{refined_count} of {total_features} features are fully refined. {needs_attention_count} need attention.**
(For each SME that has at least one feature with gaps, produce a section. Omit SMEs whose features have zero gaps.)
### {SME display name} ({feature_count} features, {gaps_count} need attention)
**{OCPSTRAT-XXX} — {Feature Summary}**
{Natural language description of ALL gaps found at every level for this feature. Cover Feature-level gaps, then Epic-level, then Story-level. Be specific and actionable. Reference epic names and story counts.}
Example tone and format:
"No size set on the feature. Epic 'CLI Monitoring' (OCPEDGE-1234) has no stories underneath. Epic 'Staging Mechanism' (OCPEDGE-1235) looks good — 5 stories, all sized. 2 stories under 'Data Pipeline' (OCPEDGE-1236) are missing story points."
(Repeat for each feature under this SME that has gaps.)
### Unassigned SME (PM action needed)
**{OCPSTRAT-XXX} — {Feature Summary}**
No SME assigned — PM needs to assign one before refinement can proceed. {Continue listing any other gaps found at Epic/Story level below this feature — do not stop at the missing SME.}
===END_SECTION===
Rules for the REFINEMENT_BY_SME section:
display_name from features.json. Features with no SME go under "Unassigned SME."