# tb-v1 — TOTAL BASES AS A COMPOUND OUTCOME (challenger) **Date:** 2026-08-02 · current ladder + champion `p_win` **byte-identical** · ledger-only · **firing verified on a real prod snapshot**. **Gates:** 4,104 tests / 329 suites green · `next build` exit 0 · tb-v1 on **10/10 total_bases props** · holdout committed, verdict n-blocked. --- ## STEP 0 — COMPONENTS CONFIRMED ON REAL DATA statsapi has **no `singles` field**. But on a real 10-game log: ``` singles = hits − doubles − triples − homeRuns singles + 2·doubles + 3·triples + 4·homeRuns == stored totalBases ✓ exact ``` **The decomposition is exact, not an approximation.** Verified before any code was written — this fix depended on it. --- ## THE MODEL Each component gets its own per-game Poisson rate; TB is their weighted sum, and the PMF is built by **exact convolution** rather than simulated (TB support is small). It inherits the **same combined multiplier** proj-v1.1 computes, so the two models differ **only in structure**. ### Why this is the fix, in one number With **identical mean TB of 1.0**: | hitter | P(TB≥2) | **P(TB≥4)** | |---|---:|---:| | pure home-run hitter | 0.221 | **0.221** | | pure singles hitter | 0.264 | **0.019** | **A 12× difference at the 4-base rung that an NB on TB alone cannot express**, because it treats one home run as four independent events. A test asserts this separation, and asserts that P(TB≥4) for a pure-HR hitter equals P(at least one HR) **exactly**. ### The approximation, stated Components are modelled as **independent** Poissons. They are not: a plate appearance that becomes a double cannot also become a single, so they are weakly negatively correlated and **independence slightly overstates the tail**. Closer to the truth than what it replaces; **not a solved problem**, and labelled `independence_caveat: true` on every record. --- ## FIRING ON PROD — 10/10 TB PROPS | | | |---|---:| | rows written | 241 | | total_bases props | 10 | | **with `proj_tb_p_over`** | **10 (100%)** | | range / sd | 0.295–0.424 / 0.045 | Real records: | player | line | ladder mean | **ladder P** | **tb-v1 P** | tb-v1 mean | rates (1B/2B/3B/HR) | games | |---|---:|---:|---:|---:|---:|---|---:| | Ketel Marte | 1.5 | 1.813 | **0.540** | **0.424** | 1.833 | .614/.208/.028/.180 | 106 | | Corbin Carroll | 1.5 | 1.814 | 0.540 | 0.424 | 1.815 | .468/.206/**.112**/.150 | 107 | | Gabriel Moreno | 1.5 | 1.590 | 0.471 | 0.405 | 1.608 | .773/.221/.000/.098 | 84 | **The means agree almost exactly** (1.813 vs 1.833) — both use the same rate machinery. **Only the shape differs**, which is precisely the intended intervention. Carroll's triples rate (0.112, far above the others) is a good face-validity check: he is a speed player, and the model sees it. ### An observation the holdout must settle **tb-v1 is systematically LOWER than the ladder** (0.424 vs 0.540 at the same mean). That is the expected *direction* — the NB overstates P(≥2) by treating a home run as four accumulating events. But **whether 0.424 is right, or an overcorrection, is not knowable from face validity.** A ~1.8-TB hitter clearing 1.5 empirically sits nearer 45–50%, which is between the two. **I am not claiming tb-v1 is better. The holdout decides.** --- ## HOLDOUT — COMMITTED, VERDICT n-BLOCKED `scripts/tb-compound-holdout.sql`, with two guards baked in: 1. **TB rows ONLY.** total_bases is 49 of 437 settled rows; averaging into other stats would hide the effect entirely. 2. **DIRECTION-ALIGNED.** `p_win` is P(graded side); both projection values are P(over). 31.4% of rows are under-graded, and the unaligned comparison is the artifact that accounted for **41%** of the ladder's apparent loss. **Settled TB rows carrying tb-v1: 0** — it went live today, on games not yet played. First settle pass is tomorrow. --- ## THE DIAGNOSIS IS ON TRIAL — recorded in advance > **If tb-v1 materially improves TB resolution** → the family-mismatch hypothesis > **HOLDS**, similarity stays **off** the critical path, and **`hits` is next** > (its own order: AB-bounded, mostly 0.5 lines, so it rides on P(0)). > > **If it does NOT improve** → the hypothesis is **WRONG**, and the > **mean-weakness / MLB-similarity branch REOPENS**. Written down now, before the result, so the verdict cannot be reinterpreted after the fact. --- ## A TRAP I HIT IN MY OWN CODE `Number(null) === 0` — again, and this time in the new module. A null component rate passed a naive finite check and was treated as a **measured zero**. That is the difference between *"this player never triples"* and *"we do not know his triple rate"*, and it would have silently narrowed the distribution. **A test caught it.** Both `tbPmf` and `tbMean` now reject `null`/`''`/boolean strictly. Recording it because this is the sixth time this specific trap has appeared in this codebase, and it caught the person who has been writing the warnings about it. ## TAGS **VERIFIED:** the TB decomposition is exact on real logs · tb-v1 fires on 10/10 TB props · means agree with the ladder (shape-only intervention) · component rates face-valid (Carroll's triples) · 12× separation at the 4-base rung. **NOT CLAIMED:** that tb-v1 is better. n-blocked until TB rows settle. **STATED LIMITATION:** component independence overstates the tail.