# MLB ARCHETYPE AXES — Layer 2

A player is a **blend across independent axes**, not one label. Skubal is a
STARTER *and* a strikeout arm *and* a ground-ball arm *and* a control arm —
four true things at once. Single-label classification is lossy, and its failure
mode is the FLEX disease: when nothing matches, invent a bucket.

**There is no fallback.** Unremarkable on an axis → absent on that axis. A
genuinely average player surfaces nothing, and says so.

## Axis independence — measured, not assumed

Correlations over the live store (467 batters PA≥50, 531 pitchers IP≥10).
**|r| ≥ 0.70 = one underlying trait → collapsed**, so one trait is never shown
as two archetypes.

| Pair | r | Decision |
|---|---|---|
| batter k% ~ whiff% | **+0.89** | collapsed → one SWING-AND-MISS axis |
| batter hard-hit% ~ avg exit velo | **+0.88** | folded into POWER as intensity |
| batter chase% ~ swing% | **+0.87** | collapsed → one AGGRESSION axis |
| batter chase% ~ bb% | **−0.72** | same axis inverted (discipline) |
| pitcher k% ~ whiff% | **+0.76** | collapsed → one STRIKEOUT axis |
| pitcher gb% ~ fb% | **−0.73** | collapsed → one signed tilt axis |
| batter barrel% ~ hard-hit% | +0.70 | at the line; barrel leads |
| **pitcher velo ~ k%** | **+0.14** | **INDEPENDENT** — velo earns its own axis |
| pitcher velo ~ whiff% | +0.07 | independent |
| pitcher velo ~ gb% | +0.07 | independent |
| **pitcher k% ~ gb%** | **−0.10** | **INDEPENDENT** — PUNCHOUT ⊥ SINKER |
| batter barrel ~ launch | +0.23 | independent |
| batter k% ~ chase% | +0.05 | independent |

The velocity result matters: **velo is not a proxy for missing bats.** A hard
thrower who misses no bats is a real, distinct type.

## Cut-lines

`p75` = distinctive · `p90` = elite, from the measured distribution — **per
role where the tails differ** even when medians agree (reliever GB% p90 = 54.1
vs starter 48.9, both median 42.5). Sample floors: **PA ≥ 50 · IP ≥ 10**.

## Every baseball name accounted for

| Name | Status | Axis / reason |
|---|---|---|
| WORKHORSE | **TIER** | role=starter + high IP; the old hardcoded default is gone |
| PUNCHOUT / WHIFF | **BUILT** | strikeout (p75 / p90) |
| SINKER / SEAM | **BUILT** | ground_ball |
| FLY BALL / ELEVATOR | **BUILT** | fly_ball |
| SURGEON ARM / PINPOINT | **BUILT** | control (low BB%) |
| NIBBLER / SCATTERGUN | **BUILT** | wild (high BB%) |
| BAIT / TRAPDOOR | **BUILT** | chase |
| HITTABLE / BATTING PRACTICE | **BUILT** | contact_allowed |
| CANNON / HOWITZER | **BUILT** | velocity — unlocked by the 53%→99% velo fix |
| SIDEARM / SUBMARINE | **BUILT** | slot (low arm angle) |
| STARTER / RELIEVER / CLOSER / SETUP | **BUILT** | role, from real usage |
| SLUGGER / BOMBER | **BUILT** | power (barrel%) |
| TECHNICIAN / SURGEON | **BUILT** | contact (low K%) |
| WHIFF RISK / WINDMILL | **BUILT** | swing_miss |
| GRINDER / SNIPER | **BUILT** | patience |
| FREE SWINGER / HACKER | **BUILT** | aggression |
| TOPSPIN / LOFT | **BUILT** | launch |
| SLASH / BARREL FINDER | **BUILT** | line_drive |
| HAMMER | **ALIAS** | of chase/strikeout — best-pitch whiff r=0.50 with overall; not independent enough for its own axis |
| BRUSH | **ALIAS** | of contact (TECHNICIAN) |
| DRIVER | **ALIAS** | of power — RBI is lineup context, not a player trait |
| **FLEX** | **RETIRED** | it was the fallback, never earned: `utility` had zero writers |
| MIRROR | **SHELVED** | needs switch-hitter axis; `bats='S'` now joined — unlock when a platoon-split feed lands |
| GHOST / BURNER / LEG | **SHELVED** | speed: SB is statsapi, not yet joined into the aggregate store |
| CATALYST | **SHELVED** | table-setter: needs lineup slot (not ingested) |
| HYBRID | **BUILT-ADJACENT** | two-way = both role profiles exist (position `TWP`) |
| ALPHA | **ALIAS** | of strikeout+control combined; the blend expresses it |
| FASTBREAK · ENFORCER · CHAINMOVER · CONDUCTOR · ARCHITECT · TORCH · LINK · ARTILLERY · BELL COW · etc. | **OTHER SPORT** | NBA/WNBA/NFL names — correctly not built here |

Zero orphans.

## Output shape

```js
{
  role, roleDetail, roleLabel, sufficient, sample, bats, throws,
  vector: { axisKey: {tier,label,value,strength} | null },  // Layer 3 reads ALL
  blend:  [ {label, axis, tier, value, strength} ],          // top ≤3 to surface
  absent: [axisKey…],   // NO DATA — distinct from unremarkable (vector null)
  note                  // honest copy when the blend is empty
}
```

`absent` vs a `null` vector entry is a real distinction: absent means we could
not measure it, null means we measured it and he is ordinary.
