# PR Statistics Deep-Dive Reference

Purpose: Statistical deep-dive of PR flow — cycle time histogram with phase decomposition, P50/P75/P90 latency reporting, contributor distribution via Lorenz curve, bot/human ratio, and large-PR risk flagging. Surfaces flow bottlenecks and concentration risk that aggregate means hide.

## Scope Boundary

- **harvest `prstats`**: distributional and risk-focused PR analytics. Histograms, percentiles, Lorenz, large-PR flags.
- **harvest `weekly` / `monthly` (elsewhere)**: aggregate counts and category breakdowns without distribution analysis.
- **harvest `dora` (elsewhere)**: 4-key metric tier classification. Lead Time at the DORA level; prstats decomposes into Pickup/Review/Merge phases.
- **harvest `okr` (elsewhere)**: outcome linkage; prstats is purely flow-statistics.
- **Pulse (elsewhere)**: live dashboards. Pulse owns ongoing tracking; prstats produces the depth report.
- **Beacon (elsewhere)**: reliability SLOs. Cycle time is delivery flow, not reliability.
- **Launch (elsewhere)**: release planning. Launch reads prstats output to size release windows.
- **Guardian (elsewhere)**: PR strategy and review policy. Guardian acts on prstats findings (large-PR thresholds, review-wait alerts).

## Workflow

```
SCOPE     →  repo, window (28-90d), branch filter, bot allowlist

COLLECT   →  gh pr list --state merged --json with timestamps + author + LOC
          →  fetch reviews timeline for first-response and approval times
          →  per_page=100 + --paginate

DECOMPOSE →  Coding (first-commit → ready-for-review)
          →  Pickup (ready → first review action)
          →  Review (first review → approval)
          →  Merge  (approval → merged)

DISTRIBUTE→  P50 / P75 / P90 per phase + total cycle
          →  histogram bins by hour up to 5d, then daily

CONCENTRATE→ Lorenz curve over PR authors; Gini coefficient
          →  bot vs human ratio (allowlist-driven)

RISK FLAG →  large PRs (>500 LOC) with review-time and defect-correlation note
          →  rubber-stamp candidates (low review time, uncorrelated with size)

REPORT    →  histograms + percentiles + Lorenz + large-PR ledger + actions
```

## Cycle Time Decomposition Benchmarks

| Phase | Elite | Good | Flag |
|-------|-------|------|------|
| Coding (first-commit → ready) | < 24h | < 3d | > 7d |
| Pickup (ready → first review) | < 6h | < 13h | > 1 business day |
| Review (first review → approval) | < 8h | < 24h | > 2 business days |
| Merge (approval → merged) | < 2h | < 8h | > 1 business day |
| Total cycle | < 26h | < 48h | > 5 business days |

(LinearB 2025 benchmarks; cycle starts at "ready for review" — draft PRs distort if measured from creation.)

## P50 / P75 / P90 Reporting

Always report three percentiles. Mean alone hides tail latency.

| Percentile | What it tells | Use for |
|------------|---------------|---------|
| P50 (median) | Typical PR experience | Headline metric |
| P75 | Upper-quartile pain | Capacity sizing |
| P90 | Tail risk | SLO-style review-wait targets |

Report P90 explicitly; if P90 is 4× P50, the team has a long-tail problem (large PRs, weekend lulls, single-reviewer bottleneck) that the median hides.

## Contributor Distribution (Lorenz / Gini)

| Gini | State | Interpretation |
|------|-------|----------------|
| 0.0 - 0.3 | Even | Healthy team distribution |
| 0.3 - 0.5 | Skewed | Normal for small teams or specialized roles |
| 0.5 - 0.7 | Concentrated | Bus-factor risk; 1-2 authors carry most flow |
| > 0.7 | Highly concentrated | Single point of failure; succession risk |

Lorenz curve plots cumulative-PRs vs cumulative-authors. Use as concentration diagnostic — never as ranking. Pair with bus-factor analysis when Gini > 0.5.

## Bot vs Human Ratio

| Ratio (bot / total) | Reading |
|---------------------|---------|
| < 10% | Low automation; review burden falls on humans |
| 10-30% | Healthy automation (Renovate, Dependabot, codegen) |
| 30-60% | Heavy automation; verify humans still see meaningful review load |
| > 60% | Bot flood; human PRs may be drowning in noise — split reports |

Maintain an explicit bot allowlist (Dependabot, Renovate, github-actions, etc.). Default heuristics (`[bot]` suffix, `type: Bot`) are a fallback only — false-negative rate is non-trivial.

## Large-PR Risk Thresholds

| Size | Classification | Risk |
|------|---------------|------|
| ≤ 200 LOC | Small | Healthy |
| 201-400 LOC | Medium | Monitor |
| 401-500 LOC | Approaching large | Suggest split |
| 501-1000 LOC | Large | 70% lower defect-detection vs small |
| > 1000 LOC | Oversized | Recommend Sherpa split + Guardian gate |

Flag any PR > 500 LOC in the report with: review-time, reviewer count, comment count, and author. When > 30% of PRs exceed 400 LOC, recommend stacked PRs (~20% throughput gain, ~8% smaller median PR size).

## Dashboard Recipe

| Panel | Source |
|-------|--------|
| Cycle time histogram (4 phase stack) | Decompose section |
| P50/P75/P90 trend (28d rolling) | Distribution section |
| Lorenz curve + Gini | Concentration section |
| Bot/human ratio (stacked area) | Bot section |
| Large-PR ledger (>500 LOC) | Risk section |
| Rubber-stamp candidates table | Risk section |

Hand off to Pulse for live rendering; Harvest produces the spec and seed data.

## Anti-Patterns

- Reporting mean cycle time only — hides long-tail PRs that drive perceived pain. Always report P50/P75/P90.
- Measuring cycle from PR creation instead of ready-for-review — draft PRs inflate the metric. Use the ready-for-review timestamp.
- Counting bot PRs as developer flow — Dependabot at 50/week makes the team look productive while masking human PR drought. Always split bot/human.
- Lorenz / Gini as performance ranking — concentration is a system property (bus-factor, role specialization), not an individual scorecard. Never use to rank.
- Single LOC threshold for "large" — 500 LOC in a config refactor and 500 LOC in core business logic carry different risk. Flag with file-type context.
- Rubber-stamp blind spot — if median review lead time is < 30min and uncorrelated with PR size, reviewers are likely not reading. Surface the correlation explicitly.
- AI-period comparison without caveat — AI inflates PR counts (+98%) and shifts size distribution larger. Comparing pre-AI P90 to post-AI P90 without the caveat is misleading (DORA 2025).
- Histogram with arithmetic bins on long-tail data — use log-spaced bins or hour-then-day stratification; arithmetic bins compress the interesting tail.
- Treating P90 as an SLO target without team agreement — Harvest reports the number; the team and Beacon set targets. Reporting an unagreed target is policy creep.

## Handoff

- **To Pulse**: histogram and percentile time-series payload for live dashboards.
- **To Beacon**: when the team wants review-wait SLOs (e.g., P90 pickup < 1 business day). Beacon owns SLO design.
- **To Guardian**: large-PR ledger and rubber-stamp candidates for review-policy adjustment.
- **To Sherpa**: oversized PR list (> 1000 LOC) with recommended split candidates.
- **To Launch**: cycle-time tail data for release-window sizing.
- **To Triage**: when data is too sparse for percentiles (< 30 PRs in window) or gh access is blocked.
