# 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.