Fetches and analyzes OpenShift component health regressions across releases. Filters by release, components, open/closed status, and date ranges; outputs JSON summaries with triage and resolution metrics.
From teamsnpx claudepluginhub openshift-eng/ai-helpers --plugin teamsThis skill uses the workspace's default tool permissions.
README.mdlist_regressions.pyGuides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Details PluginEval's skill quality evaluation: 3 layers (static, LLM judge), 10 dimensions, rubrics, formulas, anti-patterns, badges. Use to interpret scores, improve triggering, calibrate thresholds.
This skill provides functionality to fetch regression data for OpenShift components across different releases. It uses a Python script to query a component health API and retrieve regression information.
Use this skill when you need to:
Python 3 Installation
which python3Network Access
API Endpoint Configuration
base_url in list_regressions.py with the actual component health API endpointFirst, ensure Python 3 is available:
python3 --version
If Python 3 is not installed, guide the user through installation for their platform.
The script is located at:
plugins/teams/skills/list-regressions/list_regressions.py
Execute the script with appropriate arguments:
# Basic usage - all regressions for a release
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.17
# Filter by specific components
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.21 \
--components Monitoring "kube-apiserver"
# Filter by multiple components
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.21 \
--components Monitoring etcd "kube-apiserver"
# Filter by development window (GA'd release - both start and end)
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.17 \
--start 2024-05-17 \
--end 2024-10-01
# Filter by development window (in-development release - start only)
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.21 \
--start 2025-09-02
The script outputs JSON data with the following structure:
{
"summary": {
"total": <number>,
"triaged": <number>,
"triage_percentage": <number>,
"time_to_triage_hrs_avg": <number or null>,
"time_to_triage_hrs_max": <number or null>,
"time_to_resolve_hrs_avg": <number or null>,
"time_to_resolve_hrs_max": <number or null>,
"open": {
"total": <number>,
"triaged": <number>,
"triage_percentage": <number>,
"time_to_triage_hrs_avg": <number or null>,
"time_to_triage_hrs_max": <number or null>,
"open_hrs_avg": <number or null>,
"open_hrs_max": <number or null>
},
"closed": {
"total": <number>,
"triaged": <number>,
"triage_percentage": <number>,
"time_to_triage_hrs_avg": <number or null>,
"time_to_triage_hrs_max": <number or null>,
"time_to_resolve_hrs_avg": <number or null>,
"time_to_resolve_hrs_max": <number or null>
}
},
"components": {
"ComponentName": {
"summary": {
"total": <number>,
"triaged": <number>,
"triage_percentage": <number>,
"time_to_triage_hrs_avg": <number or null>,
"time_to_triage_hrs_max": <number or null>,
"time_to_resolve_hrs_avg": <number or null>,
"time_to_resolve_hrs_max": <number or null>,
"open": {
"total": <number>,
"triaged": <number>,
"triage_percentage": <number>,
"time_to_triage_hrs_avg": <number or null>,
"time_to_triage_hrs_max": <number or null>,
"open_hrs_avg": <number or null>,
"open_hrs_max": <number or null>
},
"closed": {
"total": <number>,
"triaged": <number>,
"triage_percentage": <number>,
"time_to_triage_hrs_avg": <number or null>,
"time_to_triage_hrs_max": <number or null>,
"time_to_resolve_hrs_avg": <number or null>,
"time_to_resolve_hrs_max": <number or null>
}
},
"open": [...],
"closed": [...]
}
}
}
CRITICAL: The output includes pre-calculated counts and health metrics:
summary: Overall statistics across all components
summary.total: Total number of regressionssummary.triaged: Total number of regressions triaged (open + closed)summary.triage_percentage: Percentage of all regressions that have been triaged (KEY HEALTH METRIC)summary.time_to_triage_hrs_avg: Overall average hours to triage (combining open and closed, KEY HEALTH METRIC)summary.time_to_triage_hrs_max: Overall maximum hours to triagesummary.time_to_resolve_hrs_avg: Overall average hours to resolve regressions, measured from regression opened to triage resolved timestamp (KEY HEALTH METRIC)summary.time_to_resolve_hrs_max: Overall maximum hours to resolve regressionssummary.open.total: Number of open regressions (where closed is null)summary.open.triaged: Number of open regressions that have been triaged to a JIRA bugsummary.open.triage_percentage: Percentage of open regressions triagedsummary.open.time_to_triage_hrs_avg: Average hours from opened to first triage (open only)summary.open.time_to_triage_hrs_max: Maximum hours from opened to first triage (open only)summary.open.open_hrs_avg: Average hours that open regressions have been open (from opened to current time)summary.open.open_hrs_max: Maximum hours that open regressions have been open (from opened to current time)summary.closed.total: Number of closed regressions (where closed is not null)summary.closed.triaged: Number of closed regressions that have been triaged to a JIRA bugsummary.closed.triage_percentage: Percentage of closed regressions triagedsummary.closed.time_to_triage_hrs_avg: Average hours from opened to first triage (closed only)summary.closed.time_to_triage_hrs_max: Maximum hours from opened to first triage (closed only)summary.closed.time_to_resolve_hrs_avg: Average hours from regression opened to triage resolved (JIRA bug completed), null if no valid datasummary.closed.time_to_resolve_hrs_max: Maximum hours from regression opened to triage resolved, null if no valid datacomponents: Dictionary mapping component names to objects containing:
summary: Per-component statistics (includes same fields as overall summary)open: Array of open regression objects for that componentclosed: Array of closed regression objects for that componentTime to Triage Calculation:
The time_to_triage_hrs_avg field is calculated as:
created_at timestamp in the triages arrayopened timestamp and the earliest triage timestampnull if no regressions have valid time-to-triage data in that categoryTime to Resolve Calculation:
The time_to_resolve_hrs_avg and time_to_resolve_hrs_max fields (only for triaged closed regressions) are calculated as:
resolved timestamp among the triage objectsresolved timestamp indicates when the linked JIRA bug moved to a completed stateopened timestamp and the earliest triage resolved timestampnull if no triaged closed regressions have valid resolved timestampsNote: This metric uses the triage resolved timestamp rather than the regression closed timestamp because the regression closing (bad data rolling off component readiness) can lag by a week or more after the underlying issue is fixed. The triage resolved timestamp provides a more accurate measure of how quickly a team addressed the issue.
Open Duration Calculation:
The open_hrs_avg and open_hrs_max fields (only for open regressions) are calculated as:
opened timestamp and current timenull if no open regressions have valid time dataALWAYS use these summary counts rather than attempting to count the regression arrays yourself. This ensures accuracy even when the output is truncated due to size.
The script automatically simplifies and optimizes the response:
Time field simplification (closed and last_failure):
{"Time": "2025-09-27T12:04:24.966914Z", "Valid": true}"closed": "2025-09-27T12:04:24.966914Z" (if Valid is true)"closed": null (if Valid is false)last_failure fieldField removal for response size optimization:
links: Removed from each regression (reduces response size significantly)test_id: Removed from each regression (large field, can be reconstructed from test_name if needed)Date filtering (optional):
--start and --end parameters to filter regressions to a specific time window--start YYYY-MM-DD: Excludes regressions that were closed before this date--end YYYY-MM-DD: Excludes regressions that were opened after this date--start: development_start date from get-release-dates skill (always applied)--end: GA date from get-release-dates skill (only for GA'd releases)Parse this JSON output to extract relevant information for analysis.
Based on the regression data:
summary and components.*.summary objects (do NOT count the arrays)components.*.summary.open.totalNetwork Errors
URLError or connection timeoutHTTP Errors
Invalid Release
Invalid Boolean Value
ValueError: Invalid boolean valueEnable verbose output by examining stderr:
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.17 2>&1 | tee debug.log
--release: Release version to query
"X.Y" (e.g., "4.17", "4.16")--components: Filter by component names
--components Monitoring etcd "kube-apiserver"--test-name: Filter by exact test name
--test-name "[Monitor:kubelet-container-restarts][sig-architecture] platform pods in ns/openshift-machine-config-operator should not exit an excessive amount of times"--test-name-contains: Filter by test name substring
--test-name-contains "openshift-machine-config-operator"The script outputs JSON with summaries and regressions grouped by component:
{
"summary": {
"total": 62,
"triaged": 59,
"triage_percentage": 95.2,
"time_to_triage_hrs_avg": 68,
"time_to_triage_hrs_max": 240,
"time_to_resolve_hrs_avg": 168,
"time_to_resolve_hrs_max": 480,
"open": {
"total": 2,
"triaged": 1,
"triage_percentage": 50.0,
"time_to_triage_hrs_avg": 48,
"time_to_triage_hrs_max": 48,
"open_hrs_avg": 120,
"open_hrs_max": 200
},
"closed": {
"total": 60,
"triaged": 58,
"triage_percentage": 96.7,
"time_to_triage_hrs_avg": 72,
"time_to_triage_hrs_max": 240,
"time_to_resolve_hrs_avg": 168,
"time_to_resolve_hrs_max": 480
}
},
"components": {
"Monitoring": {
"summary": {
"total": 15,
"triaged": 13,
"triage_percentage": 86.7,
"time_to_triage_hrs_avg": 68,
"time_to_triage_hrs_max": 180,
"time_to_resolve_hrs_avg": 156,
"time_to_resolve_hrs_max": 360,
"open": {
"total": 1,
"triaged": 0,
"triage_percentage": 0.0,
"time_to_triage_hrs_avg": null,
"time_to_triage_hrs_max": null,
"open_hrs_avg": 72,
"open_hrs_max": 72
},
"closed": {
"total": 14,
"triaged": 13,
"triage_percentage": 92.9,
"time_to_triage_hrs_avg": 68,
"time_to_triage_hrs_max": 180,
"time_to_resolve_hrs_avg": 156,
"time_to_resolve_hrs_max": 360
}
},
"open": [
{
"id": 12894,
"component": "Monitoring",
"closed": null,
...
}
],
"closed": [
{
"id": 12893,
"view": "4.21-main",
"release": "4.21",
"base_release": "4.18",
"component": "Monitoring",
"capability": "operator-conditions",
"test_name": "...",
"variants": [...],
"opened": "2025-09-26T00:02:51.385944Z",
"closed": "2025-09-27T12:04:24.966914Z",
"triages": [],
"last_failure": "2025-09-25T14:41:17Z",
"max_failures": 9
}
]
},
"etcd": {
"summary": {
"total": 20,
"triaged": 19,
"triage_percentage": 95.0,
"time_to_triage_hrs_avg": 84,
"time_to_triage_hrs_max": 220,
"time_to_resolve_hrs_avg": 192,
"time_to_resolve_hrs_max": 500,
"open": {
"total": 0,
"triaged": 0,
"triage_percentage": 0.0,
"time_to_triage_hrs_avg": null,
"time_to_triage_hrs_max": null,
"open_hrs_avg": null,
"open_hrs_max": null
},
"closed": {
"total": 20,
"triaged": 19,
"triage_percentage": 95.0,
"time_to_triage_hrs_avg": 84,
"time_to_triage_hrs_max": 220,
"time_to_resolve_hrs_avg": 192,
"time_to_resolve_hrs_max": 500
}
},
"open": [],
"closed": [...]
},
"kube-apiserver": {
"summary": {
"total": 27,
"triaged": 27,
"triage_percentage": 100.0,
"time_to_triage_hrs_avg": 58,
"time_to_triage_hrs_max": 168,
"time_to_resolve_hrs_avg": 144,
"time_to_resolve_hrs_max": 400,
"open": {
"total": 1,
"triaged": 1,
"triage_percentage": 100.0,
"time_to_triage_hrs_avg": 36,
"time_to_triage_hrs_max": 36,
"open_hrs_avg": 96,
"open_hrs_max": 96
},
"closed": {
"total": 26,
"triaged": 26,
"triage_percentage": 100.0,
"time_to_triage_hrs_avg": 60,
"time_to_triage_hrs_max": 168,
"time_to_resolve_hrs_avg": 144,
"time_to_resolve_hrs_max": 400
}
},
"open": [...],
"closed": [...]
}
}
}
Important - Summary Objects:
summary object contains overall pre-calculated counts for accuracycomponents object has its own summary with per-component countscomponents object maps component names (sorted alphabetically) to objects containing:
summary: Statistics for this component (total, open, closed)open: Array of open regression objects (where closed is null)closed: Array of closed regression objects (where closed has a timestamp)summary and components.*.summary fields for counts (including total, open.total, open.triaged, closed.total, closed.triaged)components.*.open or components.*.closed arrays yourselfNote: Time fields are simplified from the API response:
closed: If the regression is closed: "closed": "2025-09-27T12:04:24.966914Z" (timestamp string), otherwise nulllast_failure: If valid: "last_failure": "2025-09-25T14:41:17Z" (timestamp string), otherwise nullpython3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.17
Expected Output: JSON containing all regressions for release 4.17
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.21 \
--components Monitoring etcd
Expected Output: JSON containing regressions for only Monitoring and etcd components in release 4.21
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.21 \
--components "kube-apiserver"
Expected Output: JSON containing regressions for the kube-apiserver component in release 4.21
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.22 \
--test-name "[Monitor:kubelet-container-restarts][sig-architecture] platform pods in ns/openshift-machine-config-operator should not exit an excessive amount of times"
Expected Output: JSON containing all regressions (across all components and variants) for this exact test in release 4.22. Useful for finding the same test failing in different variant combinations.
python3 plugins/teams/skills/list-regressions/list_regressions.py \
--release 4.22 \
--test-name-contains "openshift-machine-config-operator"
Expected Output: JSON containing all regressions whose test name contains the substring (case-insensitive). Useful for finding related tests in the same namespace or sig.
Note: --test-name and --test-name-contains are mutually exclusive with each other and with --components/--team. They search across all components automatically.
The script includes a placeholder API endpoint. Update it in list_regressions.py:
# Current placeholder
base_url = f"https://teams-api.example.com/api/v1/regressions"
# Update to actual endpoint
base_url = f"https://actual-api.example.com/api/v1/regressions"
To add additional query parameters, modify the fetch_regressions function:
def fetch_regressions(release: str, opened: Optional[bool] = None,
component: Optional[str] = None) -> dict:
params = [f"release={release}"]
if opened is not None:
params.append(f"opened={'true' if opened else 'false'}")
if component is not None:
params.append(f"component={component}")
# ... rest of function
This skill is designed to be used by the /teams:analyze-regressions command, but can also be invoked directly by other commands or scripts that need regression data.
urllib module (no external dependencies)