Build Spec: pager-triage
RUN_ID: cf_rd_20260215_2021 Date: 2026-02-15 Status: APPROVED — Build from this spec alone. Estimated build time: 2 days Builder notes: This is decision-complete. Do not deviate from this spec without judge approval.
1. SKILL.md Frontmatter
---
name: pager-triage
version: 1.0.0
displayName: PagerDuty Incident Triage
description: >
AI-powered incident triage for PagerDuty and OpsGenie. List active incidents,
get incident details with timeline analysis, check on-call schedules, correlate
alerts, and acknowledge/resolve with confirmation gates. Read-only by default.
author: Anvil AI
tags:
- pagerduty
- opsgenie
- incident-response
- sre
- on-call
- devops
- enterprise
- monitoring
tools:
- name: pd_incidents
description: List active PagerDuty incidents (triggered + acknowledged)
- name: pd_incident_detail
description: Get detailed incident info including timeline, alerts, and notes
- name: pd_oncall
description: Show current on-call schedules and escalation policies
- name: pd_incident_ack
description: Acknowledge an incident (requires confirmation)
- name: pd_incident_resolve
description: Resolve an incident (requires confirmation)
- name: pd_incident_note
description: Add a note to an incident (requires confirmation)
- name: pd_services
description: List PagerDuty services and their current status
- name: pd_recent
description: Show recent incidents for a service (last 24h/7d)
---
2. Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
PAGERDUTY_API_KEY | Yes (for PD) | — | PagerDuty REST API v2 token. Recommend read-only key. |
PAGERDUTY_EMAIL | No | — | User email for write operations (ack/resolve). PD API requires From header. |
OPSGENIE_API_KEY | No | — | OpsGenie API key. Alternative to PagerDuty. |
PAGER_TRIAGE_PROVIDER | No | auto | Force provider: pagerduty, opsgenie, or auto (detect from available keys). |
Auto-detection logic: If PAGERDUTY_API_KEY is set → use PagerDuty. If OPSGENIE_API_KEY is set → use OpsGenie. If both → prefer PagerDuty (more common). If neither → error with setup instructions.
3. Subcommands & Behavior
3.1 pd_incidents — List Active Incidents
Purpose: Show all triggered and acknowledged incidents, sorted by urgency then creation time.
API Call:
curl -s \
-H "Authorization: Token token=$PAGERDUTY_API_KEY" \
-H "Content-Type: application/json" \
"https://api.pagerduty.com/incidents?statuses[]=triggered&statuses[]=acknowledged&sort_by=urgency&limit=25&include[]=assignees&include[]=services"
Output Schema:
{
"tool": "pd_incidents",
"provider": "pagerduty",
"timestamp": "2026-02-16T03:45:00Z",
"total_incidents": 3,
"incidents": [
{
"id": "P123ABC",
"incident_number": 4521,
"title": "High CPU on prod-web-03",
"status": "triggered",
"urgency": "high",
"service": {
"id": "PSVC123",
"name": "Production Web"
},
"created_at": "2026-02-16T03:00:00Z",
"duration_minutes": 45,
"assignments": [
{"name": "Jane Doe", "email": "jane@company.com"}
],
"alert_count": 3,
"escalation_level": 1,
"last_status_change": "2026-02-16T03:05:00Z"
}
],
"summary": "3 active incidents: 1 critical (triggered), 1 high (acknowledged), 1 low (triggered)"
}
Determinism: Output is machine-parseable JSON. total_incidents is an integer. incidents is always an array (empty if none). status is always one of: triggered, acknowledged. urgency is always one of: high, low.
3.2 pd_incident_detail — Incident Deep Dive
Purpose: Get full incident details including timeline (log entries), related alerts, and notes.
Parameters:
incident_id(required): PagerDuty incident ID (e.g.,P123ABC)
API Calls (sequential):
# 1. Incident details
curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
"https://api.pagerduty.com/incidents/$INCIDENT_ID?include[]=assignees&include[]=acknowledgers&include[]=conference_bridge"
# 2. Log entries (timeline)
curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
"https://api.pagerduty.com/incidents/$INCIDENT_ID/log_entries?limit=25&include[]=channels"
# 3. Alerts
curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
"https://api.pagerduty.com/incidents/$INCIDENT_ID/alerts?limit=25"
# 4. Notes
curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
"https://api.pagerduty.com/incidents/$INCIDENT_ID/notes"
Output Schema:
{
"tool": "pd_incident_detail",
"incident": {
"id": "P123ABC",
"incident_number": 4521,
"title": "High CPU on prod-web-03",
"status": "triggered",
"urgency": "high",
"service": {"id": "PSVC123", "name": "Production Web"},
"created_at": "2026-02-16T03:00:00Z",
"duration_minutes": 45,
"escalation_policy": {"id": "PESC123", "name": "Production Escalation"},
"assignments": [
{"name": "Jane Doe", "email": "jane@company.com", "escalation_level": 1}
],
"acknowledgers": [],
"description": "CPU usage exceeded 95% threshold for 5 minutes",
"conference_bridge": null
},
"timeline": [
{
"type": "trigger_log_entry",
"created_at": "2026-02-16T03:00:00Z",
"summary": "Incident triggered via Prometheus Alertmanager",
"channel_type": "api"
},
{
"type": "escalate_log_entry",
"created_at": "2026-02-16T03:05:00Z",
"summary": "Escalated to Jane Doe (Level 1)"
},
{
"type": "notify_log_entry",
"created_at": "2026-02-16T03:05:30Z",
"summary": "Notified Jane Doe via push, SMS"
}
],
"alerts": [
{
"id": "A456DEF",
"status": "triggered",
"summary": "CPU > 95% on prod-web-03",
"severity": "critical",
"created_at": "2026-02-16T03:00:00Z",
"source": "Prometheus Alertmanager",
"details": {"metric": "node_cpu_seconds_total", "value": "97.3%", "threshold": "95%"}
}
],
"notes": [],
"analysis": {
"duration": "45 minutes",
"alert_count": 3,
"escalation_count": 1,
"acknowledged": false,
"trigger_source": "Prometheus Alertmanager"
}
}
Determinism: All fields are typed and deterministic. timeline is chronologically ordered. alerts array may be empty. notes array may be empty.
3.3 pd_oncall — On-Call Schedules
Purpose: Show who's on call now, upcoming rotations, and escalation paths.
API Call:
curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
"https://api.pagerduty.com/oncalls?earliest=true&include[]=users&include[]=schedules&include[]=escalation_policies"
Output Schema:
{
"tool": "pd_oncall",
"oncalls": [
{
"user": {"name": "Jane Doe", "email": "jane@company.com"},
"schedule": {"name": "Primary SRE", "id": "PSCHED1"},
"escalation_policy": {"name": "Production Escalation", "id": "PESC123"},
"escalation_level": 1,
"start": "2026-02-15T17:00:00Z",
"end": "2026-02-16T17:00:00Z"
}
],
"summary": "2 on-call schedules active. Primary SRE: Jane Doe. Secondary SRE: Bob Smith."
}
3.4 pd_incident_ack — Acknowledge Incident ⚠️ WRITE OPERATION
Purpose: Acknowledge a triggered incident. Requires confirmation.
Parameters:
incident_id(required): PagerDuty incident ID
Confirmation Gate (MANDATORY): Before executing, the skill MUST display:
⚠️ ACKNOWLEDGE INCIDENT
ID: P123ABC (#4521)
Title: High CPU on prod-web-03
Service: Production Web
Urgency: HIGH
Duration: 45 minutes
Alerts: 3
Acknowledge this incident? This will stop escalation. [y/N]
The agent MUST NOT proceed without explicit user confirmation.
API Call (after confirmation):
curl -s -X PUT \
-H "Authorization: Token token=$PAGERDUTY_API_KEY" \
-H "From: $PAGERDUTY_EMAIL" \
-H "Content-Type: application/json" \
"https://api.pagerduty.com/incidents/$INCIDENT_ID" \
-d '{"incident": {"type": "incident_reference", "status": "acknowledged"}}'
Output Schema:
{
"tool": "pd_incident_ack",
"incident_id": "P123ABC",
"status": "acknowledged",
"acknowledged_at": "2026-02-16T03:46:00Z",
"acknowledged_by": "jane@company.com"
}
3.5 pd_incident_resolve — Resolve Incident ⚠️ WRITE OPERATION
Purpose: Resolve an incident. Requires confirmation.
Parameters:
incident_id(required): PagerDuty incident ID
Confirmation Gate (MANDATORY):
⚠️ RESOLVE INCIDENT
ID: P123ABC (#4521)
Title: High CPU on prod-web-03
Service: Production Web
Duration: 45 minutes
Alerts: 3
Resolve this incident? This marks it as fixed. [y/N]
API Call (after confirmation):
curl -s -X PUT \
-H "Authorization: Token token=$PAGERDUTY_API_KEY" \
-H "From: $PAGERDUTY_EMAIL" \
-H "Content-Type: application/json" \
"https://api.pagerduty.com/incidents/$INCIDENT_ID" \
-d '{"incident": {"type": "incident_reference", "status": "resolved"}}'
3.6 pd_incident_note — Add Incident Note ⚠️ WRITE OPERATION
Purpose: Add a note/update to an incident. Requires confirmation.
Parameters:
incident_id(required): PagerDuty incident IDcontent(required): Note text
Confirmation Gate:
⚠️ ADD NOTE TO INCIDENT P123ABC
Note: "Identified root cause as memory leak in auth-service v2.14.3. Rolling back."
Add this note? [y/N]
API Call:
curl -s -X POST \
-H "Authorization: Token token=$PAGERDUTY_API_KEY" \
-H "From: $PAGERDUTY_EMAIL" \
-H "Content-Type: application/json" \
"https://api.pagerduty.com/incidents/$INCIDENT_ID/notes" \
-d '{"note": {"content": "..."}}'
3.7 pd_services — List Services
Purpose: List all PagerDuty services with their current status (active, warning, critical, disabled).
API Call:
curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
"https://api.pagerduty.com/services?include[]=integrations&limit=100"
Output Schema:
{
"tool": "pd_services",
"services": [
{
"id": "PSVC123",
"name": "Production Web",
"status": "critical",
"description": "Production web application servers",
"created_at": "2024-01-15T00:00:00Z",
"escalation_policy": "Production Escalation",
"active_incidents": 2,
"integrations": ["Prometheus Alertmanager", "CloudWatch"]
}
],
"summary": "12 services: 1 critical, 1 warning, 10 active, 0 disabled"
}
3.8 pd_recent — Recent Incident History
Purpose: Show recent incidents for a service or across all services.
Parameters:
service_id(optional): Filter to specific servicesince(optional, default:24h): Time window —24h,7d,30d
API Call:
curl -s -H "Authorization: Token token=$PAGERDUTY_API_KEY" \
"https://api.pagerduty.com/incidents?since=$SINCE&until=now&service_ids[]=$SERVICE_ID&limit=25&sort_by=created_at:desc"
Output Schema:
{
"tool": "pd_recent",
"period": "last 24 hours",
"service": "Production Web",
"incidents": [
{
"id": "P123ABC",
"title": "High CPU on prod-web-03",
"status": "resolved",
"urgency": "high",
"created_at": "2026-02-16T03:00:00Z",
"resolved_at": "2026-02-16T04:15:00Z",
"duration_minutes": 75,
"alert_count": 3
}
],
"stats": {
"total": 5,
"by_urgency": {"high": 2, "low": 3},
"by_status": {"resolved": 4, "triggered": 1},
"mean_time_to_resolve_minutes": 42
}
}
4. OpsGenie Fallback
If OPSGENIE_API_KEY is set instead of PAGERDUTY_API_KEY, map the same subcommands to OpsGenie's API:
| Subcommand | OpsGenie Endpoint |
|---|---|
pd_incidents | GET https://api.opsgenie.com/v2/alerts?status=open |
pd_incident_detail | GET https://api.opsgenie.com/v2/alerts/{id} |
pd_oncall | GET https://api.opsgenie.com/v2/schedules/{id}/on-calls |
pd_incident_ack | POST https://api.opsgenie.com/v2/alerts/{id}/acknowledge |
pd_incident_resolve | POST https://api.opsgenie.com/v2/alerts/{id}/close |
pd_incident_note | POST https://api.opsgenie.com/v2/alerts/{id}/notes |
pd_services | GET https://api.opsgenie.com/v1/services |
OpsGenie Auth Header: Authorization: GenieKey $OPSGENIE_API_KEY
Note: OpsGenie support is a Day 2 stretch goal. Day 1 focuses entirely on PagerDuty. The output schemas should be normalized so the agent sees the same structure regardless of provider.
5. Failover Plan
| Failure | Detection | Fallback |
|---|---|---|
| PagerDuty API unreachable | HTTP timeout (10s) or 5xx response | Retry once after 2s. If still failing, report "PagerDuty API is unreachable. Check https://status.pagerduty.com/" |
| Invalid API key | HTTP 401 response | Report "Invalid PAGERDUTY_API_KEY. Create a read-only API key at Settings → API Access Keys." |
| Rate limited | HTTP 429 response | Report "PagerDuty rate limit hit. Wait 30 seconds and retry." (960 req/min limit is very generous — this should be rare.) |
pd CLI installed locally | Check which pd | If PagerDuty API fails AND pd CLI is found, fall back to pd incident:list --json, pd incident:open --json, etc. |
| No env vars set | Neither PAGERDUTY_API_KEY nor OPSGENIE_API_KEY present | Error with setup guide: "Set PAGERDUTY_API_KEY or OPSGENIE_API_KEY. See setup guide below." |
| PAGERDUTY_EMAIL not set for write ops | Missing From header | Error: "Set PAGERDUTY_EMAIL to your PagerDuty login email. Required for acknowledge/resolve operations." |
6. Security Constraints
Read-Only by Default
The following operations are always allowed without confirmation:
pd_incidents(list)pd_incident_detail(read)pd_oncall(read)pd_services(list)pd_recent(read)
Confirmation-Gated (Write Operations)
The following operations MUST display a confirmation prompt and wait for explicit user approval:
pd_incident_ack— acknowledging stops escalationpd_incident_resolve— resolving marks the incident as fixedpd_incident_note— adds permanent record to incident
Credential Handling
- API keys read from environment variables ONLY
- NEVER log, display, or include API keys in output
- NEVER include API keys in error messages
- If the user asks "what's my API key?" respond with "I don't display credentials for security. Check your PAGERDUTY_API_KEY environment variable."
API Key Permissions
The SKILL.md setup guide MUST recommend creating a read-only API key for initial setup, with write permissions added only if the user wants ack/resolve capability. Document how to create both:
- Read-only key: Settings → API Access Keys → Create → Read-only
- Full key: Settings → API Access Keys → Create → Full Access
7. Quality Gate Checklist
The following MUST pass before reviewer sign-off:
Functionality Gates
-
pd_incidentsreturns valid JSON with correct schema when incidents exist -
pd_incidentsreturns emptyincidentsarray (not error) when no active incidents -
pd_incident_detailreturns full incident with timeline, alerts, and notes -
pd_incident_detailreturns clear error for invalid incident ID -
pd_oncallreturns current on-call schedules -
pd_serviceslists services with correct status indicators -
pd_recentreturns incident history with accurate stats - All write operations show confirmation prompt before executing
- Write operations fail gracefully when
PAGERDUTY_EMAILis not set
Safety Gates
- No API key appears in any output or error message
-
pd_incident_ackcannot execute without explicit user confirmation -
pd_incident_resolvecannot execute without explicit user confirmation -
pd_incident_notecannot execute without explicit user confirmation - Invalid API key produces helpful error (not stack trace)
- Missing env vars produce setup guide (not crash)
Schema Gates
- All tool outputs are valid JSON
- All timestamps are ISO 8601
-
total_incidentsis always an integer -
incidentsis always an array -
statusvalues are from documented enum set - Empty states return empty arrays, not null
Documentation Gates
- SKILL.md includes step-by-step PagerDuty API key creation guide
- SKILL.md includes example interactions for each subcommand
- SKILL.md explains read-only vs full-access key differences
- SKILL.md includes OpsGenie setup instructions (even if Day 2)
- README includes "What's firing?" quickstart example
8. Test Plan
Smoke Tests (Run Before Every Publish)
# Test 1: List incidents (read-only)
# Expected: JSON with incidents array, summary string
# Pass if: valid JSON, no error, incidents is array
# Test 2: Get incident detail for known incident
# Expected: JSON with incident object, timeline array, alerts array
# Pass if: valid JSON, timeline is chronologically ordered
# Test 3: Show on-call schedules
# Expected: JSON with oncalls array
# Pass if: valid JSON, each oncall has user.name and schedule.name
# Test 4: List services
# Expected: JSON with services array, summary string
# Pass if: valid JSON, each service has status field
# Test 5: Recent incidents (last 24h)
# Expected: JSON with incidents array and stats object
# Pass if: valid JSON, stats.total matches incidents array length
# Test 6: Missing API key
# Expected: Helpful error message with setup instructions
# Pass if: error message includes "PAGERDUTY_API_KEY" and setup URL
# Test 7: Invalid API key
# Expected: "Invalid PAGERDUTY_API_KEY" error
# Pass if: no stack trace, clear error message
# Test 8: Write operation without PAGERDUTY_EMAIL
# Expected: Error requesting PAGERDUTY_EMAIL
# Pass if: clear error, no API call attempted
# Test 9: Confirmation gate (ack)
# Expected: Confirmation prompt displayed with incident details
# Pass if: incident title, service name, and urgency shown in prompt
# Test 10: Empty state (no active incidents)
# Expected: JSON with empty incidents array and "No active incidents" summary
# Pass if: valid JSON, incidents is empty array, no error
Integration Test (Manual, with real PD account)
- Create a test incident via PagerDuty API
- Run
pd_incidents→ verify it appears - Run
pd_incident_detail→ verify timeline shows trigger - Run
pd_incident_ack→ confirm → verify status changes - Run
pd_incident_note→ add note → verify note appears - Run
pd_incident_resolve→ confirm → verify resolution - Run
pd_recent→ verify resolved incident in history
9. SKILL.md Content Structure
The SKILL.md should be structured as follows:
# PagerDuty Incident Triage
AI-powered incident triage for PagerDuty. Read-only by default. Write operations require confirmation.
## Quick Setup
1. Go to PagerDuty → Settings → API Access Keys
2. Click "Create New API Key"
3. Name it "OpenClaw Agent" and select "Read-only" (or "Full Access" for ack/resolve)
4. Copy the key
5. Set environment variable: `export PAGERDUTY_API_KEY=your_key_here`
6. (Optional for write ops) Set: `export PAGERDUTY_EMAIL=your@email.com`
## What You Can Do
### Read Operations (always safe)
- "What's firing on PagerDuty?" → Lists all active incidents
- "Tell me about incident P123ABC" → Full details with timeline
- "Who's on call?" → Current on-call schedules
- "Show me all services" → Service health overview
- "What happened in the last 24 hours?" → Recent incident history
### Write Operations (confirmation required)
- "Acknowledge incident P123ABC" → ⚠️ Requires confirmation
- "Resolve incident P123ABC" → ⚠️ Requires confirmation
- "Add a note to incident P123ABC" → ⚠️ Requires confirmation
## Tools Reference
[... tool definitions as specified in frontmatter ...]
## OpsGenie Support
[... OpsGenie setup instructions ...]
## Security
- API keys are read from environment variables only
- Read-only mode by default
- All write operations require explicit user confirmation
- We recommend starting with a read-only API key
10. Implementation Notes for Builder
Priority Order
- pd_incidents — the "hello world" of this skill. Ship this first, test it immediately.
- pd_incident_detail — the money feature. Timeline + alert correlation is the "holy shit" moment.
- pd_oncall — simple and useful.
- pd_services — context for incidents.
- pd_recent — historical context.
- pd_incident_ack — first write operation. Get the confirmation gate pattern right here.
- pd_incident_resolve — same pattern as ack.
- pd_incident_note — same pattern, different API call.
Key Implementation Details
- PagerDuty API v2 base URL:
https://api.pagerduty.com - Auth header:
Authorization: Token token=$PAGERDUTY_API_KEY - Write ops require
From: $PAGERDUTY_EMAILheader - All responses are JSON
- Pagination: PagerDuty uses
offset/limitpagination. Defaultlimit=25is fine for most ops. Ifmore: truein response, there are additional pages. - Time format: ISO 8601 throughout. Use
since/untilquery params for time-ranged queries. - Include params: Use
include[]=query params to embed related objects (avoids extra API calls).
What NOT to Build (MVP exclusions)
- ❌ No incident creation (too dangerous for v1)
- ❌ No escalation policy modification
- ❌ No schedule management
- ❌ No maintenance window creation
- ❌ No webhook setup
- ❌ No real-time streaming/polling (just point-in-time queries)
- ❌ No multi-account support in v1
Agent Guidance in SKILL.md
Include guidance for the LLM agent on HOW to use these tools effectively:
- When user says "what's wrong?" or "what's firing?" → start with
pd_incidents - When user mentions a specific incident → use
pd_incident_detail - When triaging → show incidents first, then detail on the most urgent one
- When user wants to act → ALWAYS show confirmation with full context
- When correlating → look for incidents on the same service, similar timeframes
- Suggest checking prom-query or kube-medic for deeper investigation if those skills are installed
Related Documents
Community AI Agent Skills Discovery Sources
**Research Date:** 2026-03-26
GPU Selection Guide for Large Language Models (LLMs)
This guide helps you choose the right GPU for running Large Language Models, whether you're using them for inference, fine-tuning, or training.
ReleaseKit - Technical Requirements Document
ReleaseKit provides **two interfaces** to the same underlying functionality:
api_llm Specification
Provide direct, transparent HTTP API bindings for major LLM providers without abstraction layers or automatic behaviors.