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.
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.
インストール
Release Health Analysis をクライアントに追加します。お使いのものを選んでください。
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.
スコア
69 / 100
良好