| Tier | 8:00 | 8:30 | 9:00 | 9:30 | 10:00 | 10:30 | 11:00 | 11:30 | 12:00 | 12:30 | 13:00 | 13:30 | 14:00 | 14:30 | 15:00 | 15:30 | 16:00 | 16:30 | 17:00 | 17:30 | 18:00 | 18:30 | 19:00 | 19:30 | 20:00 | 20:30 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Tier 1 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 |
| Tier 2 | +3 | +0 | +31 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +36 | +33 | +9 | +8 | +8 | +12 |
| Tier 3 | +37 | +20 | +54 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +0 | +2 | +9 | +39 | +38 | +25 | +24 | +24 | +25 |
| Tier 4 | +17 | +15 | +26 | +20 | +22 | +22 | +20 | +11 | +9 | -19 | -49 | -47 | -76 | -70 | -71 | -73 | -42 | +18 | +19 | +18 | +18 | +18 | +12 | +11 | +11 | +12 |
Cell value = reps short (+) or spare (−) for that tier and half hour, with supply allocated top-tier-first. Color is the verdict; the number carries the same information for color-blind readers and print.
| Slot (ET) | Needed | Present | Gap | Driver |
|---|---|---|---|---|
| 9:00 | 179 | 68 | +111 | backlog release at open |
| 18:00 | 129 | 36 | +94 | late-window arrivals |
| 18:30 | 126 | 37 | +89 | late-window arrivals |
| 8:00 | 121 | 64 | +57 | backlog release at open |
| 20:30 | 81 | 33 | +48 | late-window arrivals |
| 19:00 | 81 | 36 | +45 | late-window arrivals |
| Move | Attempts recovered / day | Cost |
|---|---|---|
| Split shift: 14 Core reps to 8:00-11:00 + 17:00-21:00 | +231 | hardest to staff; biggest edge coverage |
| Move 18 reps Early to Late | +15 | no headcount change; evening differential likely |
| Start Core 60 min earlier (9:30 to 8:30) | -99 | no headcount change; shift-bid window required |
Each move is re-simulated through the same supply model, not hand-estimated. A negative number means the move sounds sensible and makes the day worse.
| Assumption | Value | Note |
|---|---|---|
| Roster | 180 reps | synthetic shift templates below |
| Fresh leads / day | 2,600 | poisson arrivals, mid-morning peak |
| Aged follow-up pool | 3,900 | worked when fresh demand allows |
| Handle time | 7.5 min / attempt | sensitivity: ±20% below |
| Occupancy target | 78% | productive share of a staffed half hour; sensitivity: ±10% |
| Attempts earned | 2.2 fresh · 1.1 follow-up | per lead on the model day |
| Calling window | 8:00 am local (TCPA) | demand is held until the lead's local window opens |
| Occ ×0.9 | Occ ×1.0 | Occ ×1.1 | |
|---|---|---|---|
| AHT ×0.8 | 91 | 75 | 62 |
| AHT ×1.0 | 131 | 111 | 95 |
| AHT ×1.2 | 171 | 147 | 127 |
The headline gap moves by double digits under modest assumption changes. That fragility is the finding: treat the gap as a scenario, not a measurement.
What this does and does not prove
It proves: under the stated assumptions, demand and supply are misaligned in a specific, repeatable shape: a surge at each timezone's legal open that the current shift mix does not follow, and a surplus in the early afternoon.
It does not prove a service failure. The gap is a scenario under an occupancy and handle-time contract. Before anyone reads it as "we are failing customers," it must survive three falsification checks:
- Measured first-response delay on leads arriving inside the gap windows (from dialer logs, not from this model).
- Front-line confirmation: do team leads actually observe queue pressure at the flagged half hours?
- Abandoned-contact rate moving with the gap, slot by slot.
If any check fails, revise the assumptions before revising the schedule. An alarming number that cannot survive its own falsification checks is not a finding; it is a liability.
| Slice | Supply side | Demand side | Status |
|---|---|---|---|
| Fresh vs aged leads | aged pool worked only in surplus slots | both demand terms stated in assumptions | modeled |
| Timezone | supply is timezone-blind (one floor) | demand held to the lead's local open (TCPA) | modeled |
| Lunches / breaks | staggered lunch dip 12:00-14:00 | n/a | modeled |
| Shrinkage (meetings, absence) | inside the occupancy constant | n/a | assumption |
| Skill routing by tier | top-tier-first allocation | tier demand weighted by priority | simplified |
| Redials / callbacks | not separated from attempts | inside attempts-per-lead | simplified |
| Weekend / holiday shape | out of scope | out of scope | out of scope |
Full method notes: docs/METHODOLOGY.md. Regenerate everything with python3 build.py (seeded).