How this skill is triggered — by the user, by Claude, or both
Slash command
/edge-scrum:release-health-analysisThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Perform the full hierarchy analysis, risk assessment, and generate all report sections, writing the results to `analysis.md` in the work directory.
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 |
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>"
}
===END_META===
===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===
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub openshift-eng/edge-tooling --plugin edge-scrum