🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

On Call Schedule Optimizer

by @charlie-morrison

Analyze and optimize on-call schedules for engineering teams. Balance load distribution, respect timezone coverage, minimize burnout, handle holidays and PTO...

TERMINAL
clawhub install on-call-schedule-optimizer

πŸ“– About This Skill


name: on-call-schedule-optimizer description: Analyze and optimize on-call schedules for engineering teams. Balance load distribution, respect timezone coverage, minimize burnout, handle holidays and PTO, and generate fair rotation schedules for PagerDuty, OpsGenie, or custom systems.

On-Call Schedule Optimizer

Build on-call schedules that don't burn people out. Analyze current rotation fairness, balance load across timezones, respect PTO and holidays, minimize after-hours pages per person, and generate optimized schedules β€” for PagerDuty, OpsGenie, or spreadsheets.

Use when: "optimize on-call schedule", "on-call rotation", "fair on-call distribution", "reduce on-call burnout", "timezone coverage", "who's on call too much", or when designing on-call for a new team.

Commands

1. analyze β€” Audit Current On-Call Schedule

#### Step 1: Extract Current Schedule

# PagerDuty API
curl -s "https://api.pagerduty.com/schedules/$SCHEDULE_ID" \
  -H "Authorization: Token token=$PD_TOKEN" \
  -H "Content-Type: application/json" | python3 -c "
import json, sys
schedule = json.load(sys.stdin)['schedule']
print(f'Schedule: {schedule[\"name\"]}')
print(f'Timezone: {schedule[\"time_zone\"]}')
for layer in schedule.get('schedule_layers', []):
    print(f'\\nLayer: {layer[\"name\"]}')
    for user in layer.get('users', []):
        print(f'  - {user[\"user\"][\"summary\"]}')
"

OpsGenie API

curl -s "https://api.opsgenie.com/v2/schedules/$SCHEDULE_ID" \ -H "Authorization: GenieKey $OG_API_KEY" | python3 -c " import json, sys data = json.load(sys.stdin)['data'] print(f'Schedule: {data[\"name\"]}') print(f'Timezone: {data[\"timezone\"]}') "

#### Step 2: Calculate Fairness Metrics

from collections import defaultdict
from datetime import datetime, timedelta

def analyze_on_call_fairness(shifts): """Analyze fairness of on-call distribution""" person_stats = defaultdict(lambda: { 'total_hours': 0, 'weekend_hours': 0, 'holiday_hours': 0, 'night_hours': 0, # 22:00-08:00 'incidents': 0, 'consecutive_days': 0, 'max_consecutive': 0, })

for shift in shifts: person = shift['person'] start = shift['start'] end = shift['end'] hours = (end - start).total_seconds() / 3600

person_stats[person]['total_hours'] += hours

# Weekend check if start.weekday() >= 5: person_stats[person]['weekend_hours'] += hours

# Night check (22:00-08:00) if start.hour >= 22 or start.hour < 8: person_stats[person]['night_hours'] += hours

# Print fairness report avg_hours = sum(s['total_hours'] for s in person_stats.values()) / len(person_stats) for person, stats in sorted(person_stats.items(), key=lambda x: -x[1]['total_hours']): deviation = ((stats['total_hours'] - avg_hours) / avg_hours) * 100 fairness = '🟒' if abs(deviation) < 10 else '🟑' if abs(deviation) < 25 else 'πŸ”΄' print(f'{fairness} {person}: {stats["total_hours"]:.0f}h total ({deviation:+.0f}%), ' f'{stats["weekend_hours"]:.0f}h weekends, {stats["night_hours"]:.0f}h nights')

#### Step 3: Generate Report

# On-Call Schedule Analysis

Fairness Score: 65/100 (⚠️ Unbalanced)

Load Distribution (last 90 days)

| Person | Total Hours | Weekends | Nights | Incidents | Deviation | |--------|------------|----------|--------|-----------|-----------| | Alice | 720h | 180h | 240h | 23 | +15% 🟑 | | Bob | 480h | 120h | 160h | 12 | -23% πŸ”΄ | | Carol | 600h | 200h | 200h | 18 | -4% 🟒 | | Dave | 720h | 100h | 240h | 28 | +15% 🟑 |

Issues Found

1. πŸ”΄ Alice and Dave carry 30% more load than Bob 2. 🟑 Carol has disproportionate weekend hours (33% vs team avg 25%) 3. 🟑 No timezone diversity β€” all US-East, gap 02:00-08:00 UTC 4. πŸ”΄ Dave had 14 consecutive on-call days last month (burnout risk)

Recommendations

1. Equalize rotation: Bob needs more shifts to balance 2. Add weekend weight: count weekend hours as 1.5Γ— for fairness 3. Cap consecutive days at 7 4. Consider follow-the-sun with EU team (if available)

2. generate β€” Create Optimized Schedule

Given team members, timezones, PTO calendar, and constraints:

  • Generate rotation that minimizes max deviation from fair share
  • Respect PTO and holidays (no on-call during approved time off)
  • Balance weekend and night hours separately
  • Ensure handoff times align with business hours for each timezone
  • Cap consecutive on-call days (default: 7)
  • 3. coverage β€” Analyze Timezone Coverage

    Map on-call coverage across 24 hours:

    00  02  04  06  08  10  12  14  16  18  20  22  24
    |---US-West---|
             |---US-East---|
                       |---EU----|
                                  |---India----|
                                            |---APAC---|
    Gap: 03:00-05:00 UTC (no primary on-call)
    

    Recommend schedule layers to fill gaps.

    4. burnout β€” Calculate Burnout Risk

    Score each team member's burnout risk based on:

  • Hours on-call in last 30/90 days
  • Incidents handled (especially 2AM+ pages)
  • Consecutive on-call days
  • PTO taken vs owed
  • After-hours page frequency
  • Flag anyone above threshold and recommend remediation (extra PTO, reduced rotation, hire).