updated 7d ago
Perform the full hierarchy analysis, risk assessment, and generate all report sections, writing the results to analysis.md in the work directory.
¿Qué puedes hacer con Release Health Analysis?
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
release-health: Analysis
Purpose
Perform the full hierarchy analysis, risk assessment, and generate all report sections, writing the results to analysis.md in the work directory.
When to Spawn
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.
Capabilities
- Jira MCP search queries (
jira_search) - File reading via
Readtool (all four phase data files + Edge Scrum Laws +.roster.json) - JSON file writing via
Writetool
This agent does not modify any Jira data.
Parameters
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 |
Instructions
1. Read Context Files
plugins/edge-scrum/.roster.jsonExtract:membersarray — useusernamefor ownership matching,sp_targetper member for capacity. Deriveroster_size(count of members) andtotal_sp_per_sprint(sum of allsp_targetvalues). If a member is missingsp_target, default that member to 8. If the file is absent, halt with an error.- Load these law files from
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.json
2. Fetch Child Issues
Split 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_10021is non-emptyblocked_by: issuelinks wheretype.inward = "is blocked by"AND blocker status not in {Closed, Verified, Done}stale: status in {In Progress, Review} ANDupdatedolder than 5 business days from{TODAY}
3. Build Hierarchy and Rollups
Group: Feature → Epics → Issues.
Orphan groups: (No Feature), (No Epic), (Unlinked Bugs).
Done states (from Laws):
- Stories/Tasks/Spikes: Closed
- OCPEDGE Bugs: Closed
- OCPBUGS Bugs: Verified, Closed
- Epics: Closed, Dev Complete
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)
4. Refinement Reassessment
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":
- Get the feature's child epics from the hierarchy
- Skip features with 0 epics — they cannot be refined via epics
- For each epic, read its
description(fromepics.json) and examine the child stories/tasks created under it (from the hierarchy) - An epic looks refined if its description describes work to be done AND the existing child stories appear to cover that described work — the stories don't need to be complete, but they should exist and address the scope outlined in the description
- If all of the feature's epics look refined by this assessment, override
epics_appear_refined = truefor that feature
Update features_refined_via_epics count accordingly.
5. Risk Assessment
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:
- Feature
sme = "None"→ 🔴 "No SME assigned" — Action: "Assign SME this sprint" - Feature
qa_contact = "None"AND status ≠ Closed → 🟡 "No QA contact" - Feature
docs_approver = "None"→ 🟢 "No docs approver" - Epic
assignee = "Unassigned"→ 🟡 "No epic DRI" - Epic
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_missingAND (spike_on_epic = trueORepics_appear_refined = true) → ✅ "Via epics" — no spike risk- If
refinement_sprint_closed = false:spike_missing→ 🔴 "No refinement spike — SME must create one in Sprint {REFINEMENT_SPRINT_NUM} that blocks this Feature"- spike exists AND NOT
spike_in_ref_sprint→ 🟡 "Spike not in refinement sprint — move to Sprint {REFINEMENT_SPRINT_NUM}" - spike exists AND
spike_in_ref_sprintAND status ≠ Closed → 🟢 "Spike in progress (expected)"
- If
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:
- Feature
has_ac = false→ 🟡 "Missing acceptance criteria" - Feature
size = "Unsized"→ 🟡 "Feature not sized" - Feature
epic_count = 0→ 🔴 "No epics — unplanned" - Epic
has_ac = false→ 🟡 "Epic missing AC" - Epic
size = "Unsized"→ 🟡 "Epic not sized" - Epic
total_issues = 0→ 🟡 "Empty epic — no stories" - Story/Task
sp null or 0→ 🟡 "Needs estimation" - Story/Task
assignee = "Unassigned"→ 🟡 "Needs owner"
7d — Blocked/Stalled:
flagged = true→ 🔴 "Impediment flagged"blocked_bynon-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:
- XL-sized epic AND
remaining_sprint_count ≤ 2→ 🔴 "XL-sized epic unlikely to complete" - L-sized epic AND
remaining_sprint_count ≤ 1→ 🔴 "L-sized epic at risk" total_remaining_sp > max_sp_capacity→ 🔴 "Capacity risk"- (if
refinement_sprint_closedANDtotal_features > 0)(features_missing_spike - features_refined_via_epics) / total_features > 0.5→ 🔴 "Systematic refinement gap" - (if
refinement_sprint_closedANDtotal_features > 0)(features_with_closed_spike + features_refined_via_epics) / total_features < 0.75→ 🟡 "Refinement coverage below 75%"
6. Write Output
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-by-SME Section (only when {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:
- No early stopping: Even if a Feature has no SME or no Epics, continue checking ALL levels and report everything found. A Feature with no SME may still have Epic and Story-level gaps worth reporting.
- Group by SME: Use the SME's
display_namefromfeatures.json. Features with no SME go under "Unassigned SME." - Omit clean features: If a Feature has zero gaps at any level, do not mention it in the SME's section.
- Natural language: Do not use tables for the per-feature descriptions. Write conversational, actionable sentences that an SME can read and act on without opening Jira.
- Docs epics excluded: In the REFINEMENT_BY_SME section only, skip epics whose summary starts with "Docs" and belong to the OSDOCS project — these are auto-generated and not part of refinement checks. Other sections (REFINEMENT_BACKLOG, standard report sections) are unaffected.
- All gap types apply: Use the same checks from sections 5b (staffing), 5c (refinement), and 5d (blocked/stalled). The REFINEMENT_BY_SME section is a reorganized view of these existing findings, not a separate analysis.
Instalación
Añade Release Health Analysis a tu cliente. Elige el que uses.
npx skills add openshift-eng/edge-toolingInstalls every skill in the repository, then prompts for which to keep.
/plugin marketplace add openshift-eng/edge-toolingAdds the repository as a plugin marketplace; install individual plugins with `/plugin install`.
git clone https://github.com/openshift-eng/edge-tooling
cp -r plugins/edge-scrum/skills/release-health-analysis ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Puntuación
69 / 100
Buena