Layer 3 Step 3: Tier-1 mappings live; Tier-2 nomination harness
PHASE 0 GATE — historical out-of-sample testing is NOT available, and the reason matters. statcast_aggregates is overwritten nightly by design (Layer 1 is a full re-pull upsert), so it holds season-TO-DATE numbers with no point-in-time history. Classifying a player for a 15 July game using today's aggregate would feed the model games from 15-21 July — look-ahead leakage, and the resulting "out-of-sample" verdict would be worthless. The harness therefore reads the archetype vector RETAINED at grade time (Session 70's instrument) and runs FORWARD-ACCRUAL, not historical. Reported rather than worked around. CANONICAL NAMES ASSERTED. Every mapping references the axis keys the classifier actually emits, and a test walks both maps against BATTER_AXES / PITCHER_AXES. A key that does not exist would look active and never fire — a mapping that appears wired while silently doing nothing is the exact failure this guards. TIER 1 IS LIVE, tautological and directional: PUNCHOUT/WHIFF raises strikeouts; SINKER/SEAM lowers home runs allowed and FLY BALL/ELEVATOR raises them (a ball on the ground cannot leave the park); SURGEON ARM/PINPOINT lowers walks allowed; SLUGGER/BOMBER raises total bases and home runs; TECHNICIAN/SURGEON raises hits and lowers strikeouts; GRINDER/SNIPER raises walks. Each adjusts only its named stat, mirrors exactly on the under side, and leaves an average player untouched. SPEED IS HONESTLY ABSENT. BURNER/stolen-bases has no axis to key on — SB is a statsapi field that never reached the aggregate store, so Layer 2 shelved it. The mapping is an empty object rather than an invented one. THE TIER-2 HARNESS tests MARGINAL CONTRIBUTION, not correlation. A ground-ball arm obviously correlates with fewer home runs; the question is whether the archetype explains the PROJECTION'S RESIDUAL (outcome minus p_win). If the projection already knows it, the residual carries no signal and the mapping is rejected as redundant — that hurdle is what catches double-counting. The split is by DATE, never random, because rows from one game share a pitcher, a park and a lineup and would leak across a random split. Direction is validated from the held-out data and a contradicted sign is REJECTED, never silently flipped to whatever the data says, which would be fitting noise. LIFECYCLE ENCODED — nominated, live, claimed. A mapping that survives runs live and is measured; only the quantified public claim waits for the ledger. Nothing sits dark. One fixture bug worth recording: my first synthetic generator aliased the carrier selector against the outcome draw and manufactured a 0.038 effect where the generator had put zero. The harness rejected it correctly — it just gave the sign reason instead of the redundancy reason, which is how I found it. The draw now uses a coprime modulus. Real candidate run end to end, GROUND-BALL to hits-allowed: INSUFFICIENT, 0 of 200 settled rows, because no settled row carries p_win yet (Session 70's instrument starts recording at the next new lock). That is the correct verdict and the expected one. Tests 3654 passed / 296 suites, web build exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VCNgGSt5qvcLxaeQqa7Zpj
This commit is contained in:
@@ -59,10 +59,23 @@ const BATTER_MAP = Object.freeze({
|
||||
stolen_bases: {},
|
||||
});
|
||||
|
||||
/**
|
||||
* TIER 1 — TAUTOLOGICAL. The archetype IS the stat tendency, so these are safe
|
||||
* in DIRECTION without an empirical test and run live from day one. They are
|
||||
* still MEASURED like everything else; only a quantified accuracy claim waits.
|
||||
* Keys below are the CANONICAL axis keys the classifier emits — a key that does
|
||||
* not exist would be a silent no-op, so `challengerMappings.test.js` asserts
|
||||
* every one against BATTER_AXES / PITCHER_AXES.
|
||||
*/
|
||||
const PITCHER_MAP = Object.freeze({
|
||||
strikeouts: { strikeout: +1, chase: +1, velocity: +1, contact_allowed: -1 },
|
||||
pitcher_strikeouts: { strikeout: +1, chase: +1, velocity: +1, contact_allowed: -1 },
|
||||
hits_allowed: { strikeout: -1, contact_allowed: +1, ground_ball: -1 },
|
||||
// Tier 1: a ground-ball arm allows fewer home runs; a fly-ball arm more.
|
||||
// The most tautological pair in the set — a ball on the ground cannot leave
|
||||
// the park.
|
||||
home_runs_allowed: { ground_ball: -1, fly_ball: +1, contact_allowed: +1 },
|
||||
home_runs: { ground_ball: -1, fly_ball: +1, contact_allowed: +1 },
|
||||
earned_runs: { contact_allowed: +1, wild: +1, strikeout: -1 },
|
||||
outs_recorded: { control: +1, ground_ball: +1, wild: -1 },
|
||||
innings_pitched: { control: +1, ground_ball: +1, wild: -1 },
|
||||
|
||||
Reference in New Issue
Block a user