updated 7d ago
You are orchestrating a release health analysis for the OCPEDGE team. Data fetching runs inline using MCP tools and transform scripts. Analysis is delegated to a sub-agent.
What can you do with Release Health?
name: release-health description: Use when analyzing the health of an OCP release cycle — evaluates Features, Initiatives, Epics, and Tasks from Jira to assess progress, identify risks, surface refinement gaps, and recommend actions to keep the team on track toward branch cut allowed-tools: Agent, AskUserQuestion, Write, Read, Glob, Bash, mcp__plugin_mcp-atlassian_mcp-atlassian__jira_get_sprints_from_board, mcp__plugin_mcp-atlassian_mcp-atlassian__jira_search user-invocable: true
Release Health Analysis
You are orchestrating a release health analysis for the OCPEDGE team. Data fetching runs inline using MCP tools and transform scripts. Analysis is delegated to a sub-agent.
Before proceeding: Read
plugins/edge-scrum/references/Edge-Scrum-Laws.mdto find which law files apply to release health orchestration. For this skill, load:laws/00-team-roster.md,laws/01-jira-projects.md,laws/05-jira-features.md,laws/06-jira-fields.md,laws/09-sprint-policies.md,laws/14-agent-conventions.md. The configuration below is derived from the Laws — when in doubt, defer to the law files.
Configuration
# Scrum Board (skill-specific; not in Laws)
board_id: "11479"
board_name: "OpenShift Edge Scrum"
sprint_prefix: ["OCPEDGE Sprint", "OpenShift Edge Sprint"]
# Custom Field IDs (Red Hat Jira instance-specific)
fields:
story_points: customfield_10028 # Numeric; Stories/Tasks/Spikes
epic_link: customfield_10014 # Story → Epic relationship
parent_link: customfield_10018 # Epic → Feature/Initiative relationship
qa_contact: customfield_10470 # User picker; QA owner
flagged: customfield_10021 # Array; non-empty = impediment
docs_approver: customfield_10473 # User picker; Doc Contact
sme: customfield_10475 # User picker; Subject Matter Expert
Execution Model
- Steps 0–1: Load laws/roster, gather release parameters (main context)
- Phase 2: Fetch sprints + features inline using MCP tools → transform scripts (main context)
- Phase 3: Fetch epics + spikes inline using MCP tools → transform scripts (main context)
- Phase 4: Delegate full analysis to sub-agent (LLM-driven risk assessment)
- Step 9: Assemble and write the final report (main context)
Rules:
- Data fetching uses MCP tools directly in the main context
- MCP responses are large and get persisted to files automatically — note those file paths
- Transform scripts (
plugins/edge-scrum/bin/) convert raw MCP data to structured JSON - Use
check-page.pyto extract pagination info from persisted files - The analysis sub-agent only needs
Read,Write, andjira_search— it consumes the structured JSON - Never embed raw Jira response data in the main context
User Arguments
The user may provide arguments: $ARGUMENTS
- Version number (e.g.,
4.19,5.0) → release version - Sprint range (e.g.,
281-285) → first through last sprint - Branch cut (e.g.,
bc:285orbranch-cut 285) → last sprint before feature freeze --refinement→ refinement-only mode (see below)- No arguments → ask for all inputs
Refinement Mode (--refinement)
When the --refinement flag is present, the skill runs a narrower analysis focused on whether Features are fully refined to the story level:
- Scope: Only Features/Initiatives in Refinement status are analyzed. Features already in Backlog, In Progress, or later states are excluded — they are past the refinement gate.
- Data collection: Phases 2–3 run identically (sprints, features, epics, spikes are all fetched).
- Analysis: Phase 4 spawns the analysis sub-agent with the
REFINEMENT_MODEparameter set totrue. The sub-agent runs all standard checks but adds aREFINEMENT_BY_SMEoutput section that groups gaps by SME with natural language summaries. - Report: Step 9 uses a condensed refinement report instead of the full release-health template. It outputs the SME-grouped refinement view plus a summary. Sections irrelevant to refinement (Sprint Forecast, Schedule Risk) are excluded.
Set REFINEMENT_MODE = true when parsing arguments if --refinement is present; otherwise REFINEMENT_MODE = false.
Workflow
Step 0: Load Edge Scrum Laws and Roster (main context)
Read both files and hold in working memory:
-
Load these law files from
plugins/edge-scrum/references/laws/:00-team-roster.md— team capacity and.roster.jsonstructure01-jira-projects.md— Jira projects and OCPBUGS components — authoritative project keys and component names05-jira-features.md— Feature/Initiative conventions and sizing06-jira-fields.md— custom field IDs09-sprint-policies.md— sprint capacity rules14-agent-conventions.md— agent orchestration conventions
-
plugins/edge-scrum/.roster.json— extract:- Team roster —
username,display_name, andsp_targetper member - Roster size — count of members (used for capacity calculations)
- If the file does not exist, stop and instruct the user to copy
.roster.json.exampleto.roster.jsonand populate it.
- Team roster —
The Laws are authoritative. Where this skill and the Laws conflict, the Laws win.
Step 1: Gather Release Parameters (main context)
Parse arguments. Use AskUserQuestion for any missing values:
- Release version — e.g.,
4.19,5.0 - Sprint range — first sprint number through last
- Branch cut sprint — which sprint is the last dev sprint
Compute and confirm:
FIRST= first sprint numberLAST= last sprint number (branch cut)TOTAL_SPRINTS= LAST − FIRST + 1REFINEMENT_SPRINT_NUM= FIRST (dedicated to spike creation, no delivery expected)TOTAL_DEV_SPRINTS= TOTAL_SPRINTS − 1TODAY= today's date (YYYY-MM-DD)
Create the work directory:
WORKDIR=/tmp/release-health-$(date +%Y%m%d) && mkdir -p $WORKDIR && echo $WORKDIR
Record WORKDIR — substitute it into all agent prompts.
Phase 2: Sprint + Feature Collection (inline)
Fetch sprints and features in parallel using MCP tools, then transform with scripts.
2a — Fetch Sprints
Call jira_get_sprints_from_board for board_id "11479" three times:
state="active"state="closed"— paginate usingpage_token(see pagination protocol below); uselimit=50state="future"
After all pages are fetched, note all persisted file paths and run:
python3 plugins/edge-scrum/bin/transform-sprints.py \
--input <all_persisted_file_paths> \
--output {WORKDIR}/sprints.json \
--today {TODAY} \
--first-sprint {FIRST} \
--last-sprint {LAST} \
--total-dev-sprints {TOTAL_DEV_SPRINTS}
2b — Fetch Features
Call jira_search with:
- JQL (standard mode):
project = OCPSTRAT AND issuetype in (Feature, Initiative) AND labels in ("ocpedge-plan", "microshift") AND "Target Version" = "openshift-{VERSION}" AND (resolution is EMPTY OR resolution not in (Duplicate, Obsolete)) ORDER BY Rank ASC - JQL (refinement mode):
project = OCPSTRAT AND issuetype in (Feature, Initiative) AND labels in ("ocpedge-plan", "microshift") AND "Target Version" = "openshift-{VERSION}" AND status = "Refinement" AND (resolution is EMPTY OR resolution not in (Duplicate, Obsolete)) ORDER BY Rank ASC - Fields:
key, summary, status, issuetype, priority, assignee, fixVersions, labels, description, issuelinks, customfield_10795, customfield_10470, customfield_10473, customfield_10475 - limit:
50
Use the refinement-mode JQL when REFINEMENT_MODE = true.
Paginate using page_token. If zero results, use fallback JQL (set fallback_used):
project = OCPSTRAT AND issuetype in (Feature, Initiative) AND labels in ("ocpedge-plan", "microshift") AND "Target Version" = "openshift-{VERSION}" AND status not in (Done, Closed) ORDER BY Rank ASC
After all pages fetched, run:
python3 plugins/edge-scrum/bin/transform-features.py \
--input <all_persisted_file_paths> \
--output {WORKDIR}/features.json
Append --fallback-used if fallback JQL was used.
2c — Verify
Read and check:
{WORKDIR}/sprints.json— if"error"key is present orsprint_mapis empty, warn the user and stop{WORKDIR}/features.json— iffeature_keysis empty:- Refinement mode: Report "All features have progressed past Refinement — nothing to check." and exit cleanly (skip Phases 3–4 and Step 9).
- Standard mode: Warn the user about scope and stop
Phase 3: Epic + Spike Collection (inline)
3a — Fetch Epics
Read {WORKDIR}/features.json. Extract feature_keys_csv.
If feature_keys has more than 50 entries, split into batches of 50. For each batch, call jira_search:
- JQL:
project in (OCPEDGE, USHIFT) AND "Parent Link" in ({feature_keys_batch_csv}) ORDER BY Rank ASC - Fields:
key, summary, status, assignee, labels, description, parent, customfield_10028, customfield_10018, customfield_10470, customfield_10473, customfield_10475 - limit:
50
Paginate using page_token. After all pages fetched, run:
python3 plugins/edge-scrum/bin/transform-epics.py \
--input <all_persisted_file_paths> \
--output {WORKDIR}/epics.json
3b — Fetch Spikes
Read {WORKDIR}/sprints.json. Extract refinement_sprint_id.
Call jira_search:
- JQL:
project in (OCPEDGE, USHIFT) AND issuetype = Spike AND sprint = {refinement_sprint_id} - Fields:
key, summary, status, assignee, issuelinks - limit:
50
Paginate using page_token. After all pages fetched, run:
python3 plugins/edge-scrum/bin/transform-spikes.py \
--input <all_persisted_file_paths> \
--features-file {WORKDIR}/features.json \
--epics-file {WORKDIR}/epics.json \
--sprints-file {WORKDIR}/sprints.json \
--output {WORKDIR}/spikes.json
3c — Verify
Read {WORKDIR}/epics.json and verify: epic_keys is a non-empty array, feature_to_epics is an object, and epics is an array. If any check fails, warn the user with a descriptive error and stop.
Pagination Protocol
This Jira instance uses page_token pagination, NOT start_at. Follow this protocol for all paginated MCP calls:
-
Make the first call without
page_token -
The response may be persisted to a file. Note the file path.
-
Run
check-page.pyto get pagination info:python3 plugins/edge-scrum/bin/check-page.py <persisted_file_path>Output:
{"issues_count": N, "has_more": bool, "next_page_token": "..."} -
If
has_moreistrue: make the next call withpage_tokenset to thenext_page_tokenvalue. Repeat from step 2. -
If
has_moreisfalse: pagination is complete.
For small responses that fit in context (not persisted), write them to {WORKDIR}/raw_<type>_<page>.json using Write, then run check-page.py on that file.
For jira_get_sprints_from_board: closed sprints may require multiple pages. Active and future typically fit in one page each.
Phase 4: Full Analysis (sub-agent)
Read plugins/edge-scrum/skills/release-health-analysis/SKILL.md. Substitute {WORKDIR}, {VERSION}, {TODAY}, {REFINEMENT_SPRINT_NUM}, {REFINEMENT_MODE}, then spawn as a sub-agent:
- Agent 4 — prompt: analysis content with all placeholders substituted
This agent reads all four data files, detects misplaced spikes, fetches child issues, builds the hierarchy, assesses all risks, and writes {WORKDIR}/analysis.md.
Step 9: Generate Report (main context)
Standard mode (REFINEMENT_MODE = false)
- Read the report template from
plugins/edge-scrum/references/release-health-report-template.md - Read
{WORKDIR}/analysis.md - Parse the
===ANALYSIS_META===block for header values - Substitute all
{placeholder}values in the template:- Header fields from release parameters and ANALYSIS_META
{executive_summary}: Write 3-5 sentences usingtop_risks,overall_health, feature counts, andsprint_recommendationfrom ANALYSIS_META. Cover: overall verdict, count on track vs. at risk, top 2-3 risks, one recommended priority this sprint.{DASHBOARD},{FEATURE_DETAIL},{EPIC_DETAIL},{RISK_REGISTER},{REFINEMENT_BACKLOG},{SPRINT_FORECAST},{ACTIONS}: paste the corresponding===SECTION:*===block content from analysis.md, stripping the sentinel lines
- Add Jira links: replace all bare issue keys (OCPSTRAT-, OCPEDGE-, USHIFT-, OCPBUGS-) with
[KEY](https://redhat.atlassian.net/browse/KEY)markdown links, skipping keys already inside link syntax - Write to
.reports/release_health_{VERSION}_{TODAY}.md - Clean up:
test -n "{WORKDIR}" && [[ "{WORKDIR}" == /tmp/release-health-* ]] && rm -rf -- "{WORKDIR}"
Refinement mode (REFINEMENT_MODE = true)
- Read
{WORKDIR}/analysis.md - Parse the
===ANALYSIS_META===block for header values - Extract the
===SECTION:REFINEMENT_BY_SME===block from analysis.md. If the section is not found, halt with an error: "Analysis phase did not produce the REFINEMENT_BY_SME section — check sub-agent output." - Extract the
===SECTION:REFINEMENT_BACKLOG===block from analysis.md - Build the refinement report using the template from
plugins/edge-scrum/references/refinement-report-template.md - Substitute placeholders in the template:
{VERSION},{TODAY},{REFINEMENT_SPRINT_NUM}— from release parameters{refinement_state}— derive fromsprints.json:"Closed"ifrefinement_sprint_closed = true, otherwise"Open"{total_features}— from ANALYSIS_METAtotal_features{refined_count}— from ANALYSIS_METArefined_count{needs_attention_count}— from ANALYSIS_METAneeds_attention_count{REFINEMENT_BY_SME}— paste the extracted section content, stripping sentinel lines{REFINEMENT_BACKLOG}— paste the extracted section content, stripping sentinel lines
- Add Jira links (same rule as standard mode)
- Write to
.reports/refinement_{VERSION}_{TODAY}.md - Clean up:
test -n "{WORKDIR}" && [[ "{WORKDIR}" == /tmp/release-health-* ]] && rm -rf -- "{WORKDIR}"
Edge Cases
- No Features found: Try fallback JQL (handled in Phase 2b); warn user to confirm scope; stop if still empty. In refinement mode, an empty result means all features have progressed past Refinement — handled in Phase 2c.
- Feature with no Epics: Flag as "Unplanned"; epic fetch returns empty list for that feature.
- Epic with no Stories: Flag as "Empty" in analysis.
- Version format varies (
4.19vs4.19.0): Analysis agent tries both in fixVersion queries. - SME / Docs field errors: Transform scripts handle gracefully with fallback values.
- Sprint resolution failure: transform-sprints.py sets
"error"in JSON; main context stops before Phase 3. - Multiple spikes per Feature: transform-spikes.py records all in
spike_keys[]; analysis agent uses worst-case status. - OCPBUGS without Epics: Grouped under "(Unlinked Bugs)" section by analysis agent.
- Analysis during refinement sprint: Analysis agent skips 7a entirely — no delivery progress expected.
- Spike linked to child Epic: Detected by analysis agent via
all_ref_sprint_spikescross-reference withepics.json.
Important Notes
- Read-only: This skill does not modify any Jira data.
- Transform scripts:
plugins/edge-scrum/bin/— reusable data transformation (no LLM needed) - Analysis sub-agent:
plugins/edge-scrum/skills/release-health-analysis/SKILL.md— LLM-driven risk assessment and report generation - Work directory:
{WORKDIR}persists across phases within a run. Rerunning on the same day overwrites prior files. - Laws files: The analysis sub-agent reads specific files from
plugins/edge-scrum/references/laws/per the index atplugins/edge-scrum/references/Edge-Scrum-Laws.md— never hardcode roster, rules, or sizing in skill definitions.
Install
Add Release Health to your client. Pick the one you use.
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 ~/.claude/skills/A skill is a plain directory. Copy it into `.claude/skills/` in a project or in your home directory.
Score
79 / 100
Good