mlb-category-state-analyzer

v2026.09.24

Computes the weekly category state for a Yahoo H2H Categories matchup across all 10 scoring categories (R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV). Pulls current totals from Yahoo, builds rest-of-week per-cat projections from roster + schedule, then DELEGATES matchup/per-cat win-probability math to `matchup-win-probability-sim`. Consumes the sim's `per_cat_win_probability` and `matchup_win_probability` to derive cat_position, cat_pressure, cat_reachability, and cat_punt_score, and emits a "push 6, punt N" plan that drives waiver, streaming, and lineup decisions. Use when user asks about "category state", "where am I winning", "should I punt", "matchup score", "cat pressure", weekly category planning, or which cats to push vs. concede.

GitHub
Install command
npx skhub add lyndonkl/mlb-category-state-analyzer
Markdown
SKILL.md

MLB Category State Analyzer

Table of Contents

Example

Scenario: Week 3, K L's Boomers (Team 5) vs. Los Doyers. Wednesday AM (mid-week). 4 scoring days remain.

Raw matchup scores (pulled from Yahoo matchup page):

CatUsOppMarginGames left (us/opp)
R2831-326 / 22
HR97+226 / 22
RBI3029+126 / 22
SB46-226 / 22
OBP.342 (82 PA).336 (78 PA)+.00626 / 22 GP
K4238+49 SP starts / 7 SP starts
ERA3.80 (21 IP)4.12 (19 IP)-0.32 (better)9 / 7
WHIP1.18 (21 IP)1.25 (19 IP)-0.07 (better)9 / 7
QS21+19 / 7
SV35-2~8 RP days / ~8 RP days

Projections built for the sim (rest-of-week {mean, stddev} — see resources/methodology.md):

CatOur projectionOpp projection
Rfinal 52 ± 9final 57 ± 8
HRfinal 15 ± 3.5final 13 ± 3.2
RBIfinal 52 ± 9final 55 ± 8
SBfinal 6 ± 2.3final 10 ± 2.5
OBP.346 ± .015.341 ± .014
Kfinal 96 ± 11final 85 ± 10
ERA3.88 ± 0.404.05 ± 0.45
WHIP1.20 ± 0.071.25 ± 0.08
QSfinal 6.1 ± 1.5final 3.8 ± 1.4
SVfinal 4.8 ± 1.4final 7.7 ± 1.5

Delegate to matchup-win-probability-sim with:

  • cat_list = [R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV]
  • cat_inverse_list = [ERA, WHIP]
  • cat_win_threshold = 6
  • our_per_cat_projection / opp_per_cat_projection from the table above
  • sim_mode = "monte_carlo", n_simulations = 10000, random_seed = 42

Sim output (consumed by this skill):

  • matchup_win_probability = 0.58
  • per_cat_win_probability: R 0.36, HR 0.65, RBI 0.42, SB 0.16, OBP 0.60, K 0.74, ERA 0.62, WHIP 0.68, QS 0.85, SV 0.10
  • expected_cats_won = 5.18

Per-cat signals (derived here from sim output + baseball state — see resources/methodology.md):

CatPosition (from state)Pressure (state + pace)Reachability (= round(100 × p_cat))Punt Score (= f(1 − p_cat) + volatility)Verdict
Rlosing723644push (contested)
HRwinning486521maintain
RBIwinning (thin)654235push
SBlosing551658evaluate punt
OBPwinning (thin)706024push
Kwinning557416push
ERAwinning626223push
WHIPwinning606819maintain
QSwinning78859push hard
SVlosing381084punt

Overall recommendation: Push 6, maintain 2, punt 2. Matchup win prob 58% (neutral favorite).

  • Push (6): HR, OBP, K, ERA, QS — each has per_cat_win_probability ≥ 0.60. Plus RBI as the contested-but-reachable 6th.
  • Maintain (2): WHIP (locked-ish), R (reachability lowish but not a true punt).
  • Punt (2): SB (p = 0.16, low reach) and SV (p = 0.10 + volatility bonus → punt score 84).

Downstream implications for other agents:

  • Lineup optimizer: matchup_win_probability = 0.58 → neutral-to-favorite, standard daily_quality optimization (no variance tilt).
  • Waiver analyst: prioritize SP (QS, K, ERA), OBP-heavy bats; not closers or speed specialists.
  • Streaming strategist: every QS-capable SP starts; skip any 5-inning risk arm.

Workflow

Copy this checklist and track progress:

MLB Category State Analysis Progress:
- [ ] Step 1: Pull current matchup scores from Yahoo
- [ ] Step 2: Count remaining games/PAs/IP for both rosters
- [ ] Step 3: Build per-cat projection dicts ({mean, stddev}) for both rosters
- [ ] Step 4: Delegate to matchup-win-probability-sim (pass cat_list, projections, threshold=6, inverse=[ERA,WHIP])
- [ ] Step 5: Derive cat_position (from state), cat_pressure, cat_reachability, cat_punt_score from sim output
- [ ] Step 6: Rank cats and emit push/maintain/punt plan
- [ ] Step 7: Write signal file with YAML frontmatter (include matchup_win_probability from sim)

Step 1: Pull current matchup scores

Web-fetch the Yahoo matchup page: https://baseball.fantasysports.yahoo.com/b1/23756/5/matchup?week=N. Extract current totals for both teams in each of the 10 cats. For ratio cats (OBP, ERA, WHIP), also capture the denominator (PAs for OBP, IP for ERA/WHIP). This is required — you cannot build a ratio-cat projection without the volume underlying the ratio.

  • 5 batting cats: R, HR, RBI, SB, OBP (+ at-bats / plate-appearances)
  • 5 pitching cats: K, ERA, WHIP, QS, SV (+ innings pitched)
  • Source URL cited in signal file

See resources/methodology.md for scrape procedure and fallback if Yahoo is unreachable.

Step 2: Count remaining games/PAs/IP

For each roster, count the number of MLB games its players will play for the rest of the scoring period, and project PAs (hitters) and IP (pitchers).

  • Hitter games remaining: sum of (each rostered hitter's team games × probability they start)
  • Pitcher starts remaining: number of scheduled SP starts for the rest of the week per roster
  • Reliever days remaining: days × eligible RPs (for SV projection)
  • Volume imbalance: if one team has meaningfully more games, that will show up directly in the projection means (and so in per_cat_win_probability)

Use MLB.com schedules + probable pitcher grids. See resources/methodology.md.

Step 3: Build per-cat projection dicts

For each team, build a dict {cat: {mean, stddev}} where mean is the projected final (or remaining, consistently used across both teams — pick one convention) and stddev reflects uncertainty given remaining volume.

  • Counting cats (R, HR, RBI, SB, K, QS, SV): mean = current_total + Σ(per-player per-game rate × games remaining × daily_quality). stddev ≈ 0.35 × expected_remaining as a default CV.
  • Ratio cats (OBP, ERA, WHIP): mean = (current_ratio × current_volume + projected_remaining_ratio × remaining_volume) / total_volume. stddev ≈ σ_per_obs / sqrt(total_volume) — shrinks as total IP/PA grows.
  • Both dicts have identical keys and the exact league cat_list.
  • Use OBP (not AVG) and qs_probability (not W) from upstream mlb-player-analyzer signals — see Guardrails.

See resources/methodology.md.

Step 4: Delegate to matchup-win-probability-sim

Invoke the sibling skill with a well-formed input payload:

inputs to matchup-win-probability-sim:
  cat_list:           [R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV]
  cat_inverse_list:   [ERA, WHIP]
  cat_win_threshold:  6
  our_per_cat_projection:  <dict from Step 3>
  opp_per_cat_projection:  <dict from Step 3>
  sim_mode:           "monte_carlo"
  n_simulations:      10000
  random_seed:        42
  tie_rule:           "half"

outputs consumed:
  matchup_win_probability  (float in [0,1])
  per_cat_win_probability  (dict[cat, float])
  expected_cats_won        (float)
  variance_estimate        (float)
  • All 10 cats present in both projection dicts
  • cat_inverse_list = [ERA, WHIP] (lower-is-better)
  • cat_win_threshold = 6 (Yahoo 10-cat majority)
  • Seed passed for reproducibility
  • Sim output fields captured and stored for Step 5

Step 5: Derive per-cat signals from sim output + state

Apply the formulas in resources/methodology.md. The sim owns the probability math; this skill owns the baseball-state interpretation.

  • cat_position ∈ {winning, tied, losing} — computed locally from current totals (not sim). Ratio-cat direction handled (OBP higher = winning; ERA/WHIP lower = winning).
  • cat_pressure (0–100) — simple arithmetic from position + close-margin + volume-edge + locked-in flags. See pressure formula in Quick Reference.
  • cat_reachability (0–100) — now = round(100 × per_cat_win_probability[cat]), taken directly from the sim.
  • cat_punt_score (0–100) — (100 × (1 − per_cat_win_probability[cat])) × 0.6 + 30 × is_volatile + 20 × below_min_threshold − 10 × has_spillover, clamped.

Step 6: Rank and emit plan

Rank all 10 cats by cat_pressure × cat_reachability / 100:

  • Top 6: push — mark these as priority for waivers, streams, starts
  • Middle 2: maintain — hold position, don't overspend
  • Bottom 2: evaluate punt — if cat_punt_score > 60, confirm punt; otherwise hold

Goal in H2H Cats is 6-of-10. A defensible plan is "push 6, concede up to 4." See resources/template.md for the output signal format.

Step 7: Write signal file

Write to signals/YYYY-MM-DD-cat-state.md with YAML frontmatter (type: cat-state). Include matchup_win_probability from the sim as a top-level field. Validate with mlb-signal-emitter before persisting.

  • All 10 cats present with all 4 signals each
  • matchup_win_probability and expected_cats_won recorded in frontmatter
  • sim_meta block (sim_mode, n_simulations, random_seed) recorded for reproducibility
  • Confidence reflects data quality (lower if Yahoo scrape was partial)
  • source_urls includes Yahoo matchup page + MLB.com schedule pages + a reference to the sim skill
  • Red-team findings noted (e.g., "Opp has a two-start ace coming that could flip K + ERA + WHIP all at once")

Validate output using resources/evaluators/rubric_mlb_category_state_analyzer.json. Minimum: average score of 3.5 or above.

Common Patterns

Pattern 1: Balanced mid-week state

  • Typical Wednesday AM state: 3-4 cats already locked, 3-4 close, 2-3 volatile.
  • Action: push the close cats hardest, coast the locked wins, ignore locked losses.
  • The sim's per-cat probs already reflect this — cats with p ∈ [0.40, 0.65] are the contested ones.

Pattern 2: Volume-imbalanced matchup

  • We have 30 hitter games left, opp has 22. Our counting-cat projection means rise; sim's per_cat_win_probability for R/HR/RBI/SB rises accordingly.
  • Action: stack the lineup (fewer off-days, prefer teams playing doubleheaders), bid on streamers. Pressure boost comes from the volume-edge flag, reachability boost comes automatically from the sim.

Pattern 3: Two-start ace incoming (us or them)

  • One pitcher's two-start week can swing K, ERA, WHIP, QS simultaneously.
  • Encode this in the projection dict: their expected IP and K rise, ERA/WHIP means improve (toward their ERA/WHIP), QS mean rises by ~0.45 per expected QS-quality start.
  • The sim then shows 4 pitching cats moving together in per_cat_win_probability deltas.

Pattern 4: Save-category volatility

  • SVs are low-frequency; one walkoff blown save flips the category.
  • In the projection dict, use a low mean (≤ 2.5/week per locked closer) and moderate stddev (≥ 1.2). The sim will naturally report per_cat_win_probability near 0.1–0.25 when behind by 2+.
  • The +30 volatility bonus in cat_punt_score (applied here, not in the sim) pushes SV to punt when sim reachability agrees.

Pattern 5: Ratio-cat "freeze"

  • Late in the week, if opp is far below the IP/PA minimum (e.g., has 9 IP on Friday with no more starts), their ratio cats are locked at whatever they have.
  • Encode by setting opp ratio-cat stddev near zero and their mean at a punitive-or-forfeited value. The sim then returns per_cat_win_probability ≈ 1.0 for those cats.

Guardrails

  1. Never compute OBP/ERA/WHIP from rates alone — always include volume (PA/IP). A .400 OBP in 10 PAs is not better than .342 in 82 PAs. The projection-dict mean/stddev for ratio cats must come from the weighted-average formula; the sim takes those as truth.

  2. QS is the #1 category, not Wins. This league uses Quality Starts (6+ IP, ≤3 ER). A 5-inning outing scores zero. When projecting remaining QS, multiply each SP start by its QS probability (from mlb-player-analyzer's qs_probability signal) — don't just count scheduled starts.

  3. OBP is the #5 category, not AVG. Walks count. When projecting OBP contribution, use players' OBP (not AVG). A high-BB, low-AVG player like Juan Soto is worth more in this league than his raw hit rate suggests.

  4. SV is volatile — trust the punt when signals agree. Unlike counting batting cats, a 2-save deficit with 3 days left has low per_cat_win_probability regardless of roster. Don't fight for saves if the closer role on your roster isn't locked (check save_role_certainty < 70 → automatic punt candidate). The volatility bonus in cat_punt_score is applied here, not in the sim — the sim returns raw probability.

  5. cat_reachability comes from the sim — don't recompute. This is a delegation. If the sim returns per_cat_win_probability[R] = 0.36, then cat_reachability[R] = 36. Do not apply z-score shortcuts or best/worst-case buckets here — those lived in the old heuristic and are now owned by the sim skill.

  6. Locked-in cats get pressure adjustments, not zero. A locked-in win still has cat_pressure ≈ 40 (it's banked). A locked-in loss still has cat_pressure ≈ 20 (stop investing). Don't set them to zero — downstream agents use non-zero values to decide bench vs. drop.

  7. Ratio cats need the minimum-IP/PA rule. Yahoo enforces minimums for pitcher ratio cats (usually 20 IP for the week). If either roster is tracking below the minimum late in the week, the ratio cat may auto-loss. Encode this in the projection dict (stddev → 0, mean → punitive) before calling the sim, AND add +20 below_min_threshold to cat_punt_score.

  8. Never re-derive upstream signals. qs_probability, sb_opportunity, obp_contribution, save_role_certainty come from mlb-player-analyzer. Read them from the signal directory; do not recompute.

  9. Always pass a random_seed to the sim. Without it, two runs of this skill produce slightly different cat_reachability values, which will confuse downstream agents doing diff comparisons. Default seed: 42.

Quick Reference

Where the math lives now:

SignalOwnerFormula
cat_positionthis skillenum from current totals (ratio-direction aware)
cat_pressurethis skillbaseline 50 + 20 × close + 15 × vol-edge − 10 × locked_win − 30 × locked_loss
cat_reachabilitydelegated to matchup-win-probability-sim= round(100 × per_cat_win_probability[cat])
cat_punt_scorethis skill (uses sim output)(100 × (1 − p_cat)) × 0.6 + 30 × volatile + 20 × below_min − 10 × spillover
matchup_win_probabilitydelegated to matchup-win-probability-simMonte Carlo P(cats_won ≥ 6)
cat_pressure =
    50                                         # neutral baseline
  + 20 × (is_close_margin: deficit/lead ≤ 10% of total)
  + 15 × (opponent_volume_exhausted: we have more games left)
  - 10 × (locked_in_win)
  - 30 × (locked_in_loss)
  clamp(0, 100)

cat_reachability = round(100 × per_cat_win_probability[cat])   # from sim

cat_punt_score =
    (100 - cat_reachability) × 0.6                      # base: if we can't reach, consider punting
  + 30 × (cat is traditionally volatile: SV)
  + 20 × (below min-PA/IP threshold)
  - 10 × (cat has spillover: K→QS, OBP→R, HR→R+RBI)
  clamp(0, 100)

League constants (from context/league-config.md):

  • 10 cats: R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV
  • Inverse cats: ERA, WHIP (lower-is-better — passed as cat_inverse_list to the sim)
  • OBP (not AVG) — walks matter
  • QS (not W) — 6+ IP with ≤3 ER
  • H2H Cats, goal = win 6+ of 10 each week (cat_win_threshold = 6)
  • Daily lineup lock; weekly matchup rolls Mon-Sun

Signal file output schema (from context/frameworks/signal-framework.md):

---
type: cat-state
date: YYYY-MM-DD
emitted_by: mlb-category-state-analyzer
week: N
matchup_opponent: <team name>
scoring_days_remaining: N
matchup_win_probability: 0.58          # from matchup-win-probability-sim
expected_cats_won: 5.18                # from matchup-win-probability-sim
sim_meta:
  sim_mode: monte_carlo
  n_simulations: 10000
  random_seed: 42
synthesis_confidence: 0.0-1.0
source_urls:
  - https://baseball.fantasysports.yahoo.com/b1/23756/5/matchup?week=N
---

Body: per-cat table + overall push/maintain/punt recommendation + red-team findings.

Thresholds used downstream:

AgentThresholdEffect
Waiver analystcat_pressure ≥ 60Prioritize targets that fill that cat
Streaming strategistcat_pressure (ERA/WHIP) < 30Allow riskier streamers (we're punting)
Lineup optimizermatchup_win_probability < 0.4 / > 0.6Variance-seek as underdog / damp as favorite
Trade analyzerweights trade_cat_deltaMultiplied by cat_pressure / 50

Key resources:

Inputs required:

  • Current matchup scores (10 cats, both teams, with volume for ratio cats)
  • Roster IDs for both teams
  • Remaining MLB schedule through Sunday
  • Upstream signals: qs_probability, save_role_certainty, obp_contribution, sb_opportunity, daily_quality
  • League config (cats list, min-IP/PA thresholds, cat_win_threshold)

Outputs produced:

  • signals/YYYY-MM-DD-cat-state.md — signal file with 10-cat table, overall plan, matchup_win_probability, confidence, source URLs
  • cat_position, cat_pressure, cat_reachability, cat_punt_score per cat
  • Overall "push N, maintain M, punt P" recommendation (N + M + P = 10, target N ≥ 6)
  • matchup_win_probability (from sim delegate) for lineup-optimizer variance decisions
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

Not specified

Source path

skills/mlb-category-state-analyzer

Default branch

main

Latest commit

4acc337

Tree SHA

4f0a83e