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):
| Cat |
Us |
Opp |
Margin |
Games left (us/opp) |
| R |
28 |
31 |
-3 |
26 / 22 |
| HR |
9 |
7 |
+2 |
26 / 22 |
| RBI |
30 |
29 |
+1 |
26 / 22 |
| SB |
4 |
6 |
-2 |
26 / 22 |
| OBP |
.342 (82 PA) |
.336 (78 PA) |
+.006 |
26 / 22 GP |
| K |
42 |
38 |
+4 |
9 SP starts / 7 SP starts |
| ERA |
3.80 (21 IP) |
4.12 (19 IP) |
-0.32 (better) |
9 / 7 |
| WHIP |
1.18 (21 IP) |
1.25 (19 IP) |
-0.07 (better) |
9 / 7 |
| QS |
2 |
1 |
+1 |
9 / 7 |
| SV |
3 |
5 |
-2 |
~8 RP days / ~8 RP days |
Projections built for the sim (rest-of-week {mean, stddev} — see resources/methodology.md):
| Cat |
Our projection |
Opp projection |
| R |
final 52 ± 9 |
final 57 ± 8 |
| HR |
final 15 ± 3.5 |
final 13 ± 3.2 |
| RBI |
final 52 ± 9 |
final 55 ± 8 |
| SB |
final 6 ± 2.3 |
final 10 ± 2.5 |
| OBP |
.346 ± .015 |
.341 ± .014 |
| K |
final 96 ± 11 |
final 85 ± 10 |
| ERA |
3.88 ± 0.40 |
4.05 ± 0.45 |
| WHIP |
1.20 ± 0.07 |
1.25 ± 0.08 |
| QS |
final 6.1 ± 1.5 |
final 3.8 ± 1.4 |
| SV |
final 4.8 ± 1.4 |
final 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):
| Cat |
Position (from state) |
Pressure (state + pace) |
Reachability (= round(100 × p_cat)) |
Punt Score (= f(1 − p_cat) + volatility) |
Verdict |
| R |
losing |
72 |
36 |
44 |
push (contested) |
| HR |
winning |
48 |
65 |
21 |
maintain |
| RBI |
winning (thin) |
65 |
42 |
35 |
push |
| SB |
losing |
55 |
16 |
58 |
evaluate punt |
| OBP |
winning (thin) |
70 |
60 |
24 |
push |
| K |
winning |
55 |
74 |
16 |
push |
| ERA |
winning |
62 |
62 |
23 |
push |
| WHIP |
winning |
60 |
68 |
19 |
maintain |
| QS |
winning |
78 |
85 |
9 |
push hard |
| SV |
losing |
38 |
10 |
84 |
punt |
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.
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).
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.
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)
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.
Step 6: Rank and emit plan
Rank all 10 cats by cat_pressure × cat_reachability / 100:
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.
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
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.
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.
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.
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.
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.
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.
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.
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.
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:
| Signal |
Owner |
Formula |
cat_position |
this skill |
enum from current totals (ratio-direction aware) |
cat_pressure |
this skill |
baseline 50 + 20 × close + 15 × vol-edge − 10 × locked_win − 30 × locked_loss |
cat_reachability |
delegated to matchup-win-probability-sim |
= round(100 × per_cat_win_probability[cat]) |
cat_punt_score |
this skill (uses sim output) |
(100 × (1 − p_cat)) × 0.6 + 30 × volatile + 20 × below_min − 10 × spillover |
matchup_win_probability |
delegated to matchup-win-probability-sim |
Monte 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:
| Agent |
Threshold |
Effect |
| Waiver analyst |
cat_pressure ≥ 60 |
Prioritize targets that fill that cat |
| Streaming strategist |
cat_pressure (ERA/WHIP) < 30 |
Allow riskier streamers (we're punting) |
| Lineup optimizer |
matchup_win_probability < 0.4 / > 0.6 |
Variance-seek as underdog / damp as favorite |
| Trade analyzer |
weights trade_cat_delta |
Multiplied by cat_pressure / 50 |
Key resources:
- resources/template.md: Output signal file format, per-cat table, sim-integration worked example
- resources/methodology.md: Yahoo scrape procedure, remaining-games projection, building per-cat projection dicts, sim-integration formulas
- resources/evaluators/rubric_mlb_category_state_analyzer.json: Evaluator rubric
- Sibling skill:
matchup-win-probability-sim — owns per-cat and matchup-level win-probability math via Monte Carlo / Poisson-binomial
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
1---2name: mlb-category-state-analyzer3description: 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.4---5# MLB Category State Analyzer
6
7## Table of Contents
8- [Example](#example)
9- [Workflow](#workflow)
10- [Common Patterns](#common-patterns)
11- [Guardrails](#guardrails)
12- [Quick Reference](#quick-reference)
13
14## Example
15
16**Scenario**: Week 3, K L's Boomers (Team 5) vs. Los Doyers. Wednesday AM (mid-week). 4 scoring days remain.
17
18**Raw matchup scores (pulled from Yahoo matchup page)**:
19
20| Cat | Us | Opp | Margin | Games left (us/opp) |
21|---|---|---|---|---|
22| R | 28 | 31 | -3 | 26 / 22 |
23| HR | 9 | 7 | +2 | 26 / 22 |
24| RBI | 30 | 29 | +1 | 26 / 22 |
25| SB | 4 | 6 | -2 | 26 / 22 |
26| OBP | .342 (82 PA) | .336 (78 PA) | +.006 | 26 / 22 GP |
27| K | 42 | 38 | +4 | 9 SP starts / 7 SP starts |
28| ERA | 3.80 (21 IP) | 4.12 (19 IP) | -0.32 (better) | 9 / 7 |
29| WHIP | 1.18 (21 IP) | 1.25 (19 IP) | -0.07 (better) | 9 / 7 |
30| QS | 2 | 1 | +1 | 9 / 7 |
31| SV | 3 | 5 | -2 | ~8 RP days / ~8 RP days |
32
33**Projections built for the sim** (rest-of-week `{mean, stddev}` — see [resources/methodology.md](resources/methodology.md#building-per-cat-projection-dicts)):
34
35| Cat | Our projection | Opp projection |
36|---|---|---|
37| R | final 52 ± 9 | final 57 ± 8 |
38| HR | final 15 ± 3.5 | final 13 ± 3.2 |
39| RBI | final 52 ± 9 | final 55 ± 8 |
40| SB | final 6 ± 2.3 | final 10 ± 2.5 |
41| OBP | .346 ± .015 | .341 ± .014 |
42| K | final 96 ± 11 | final 85 ± 10 |
43| ERA | 3.88 ± 0.40 | 4.05 ± 0.45 |
44| WHIP | 1.20 ± 0.07 | 1.25 ± 0.08 |
45| QS | final 6.1 ± 1.5 | final 3.8 ± 1.4 |
46| SV | final 4.8 ± 1.4 | final 7.7 ± 1.5 |
47
48**Delegate to `matchup-win-probability-sim`** with:
49- `cat_list = [R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV]`
50- `cat_inverse_list = [ERA, WHIP]`
51- `cat_win_threshold = 6`
52- `our_per_cat_projection` / `opp_per_cat_projection` from the table above
53- `sim_mode = "monte_carlo"`, `n_simulations = 10000`, `random_seed = 42`
54
55**Sim output (consumed by this skill)**:
56- `matchup_win_probability = 0.58`
57- `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
58- `expected_cats_won = 5.18`
59
60**Per-cat signals (derived here from sim output + baseball state — see [resources/methodology.md](resources/methodology.md#signal-formulas))**:
61
62| Cat | Position (from state) | Pressure (state + pace) | Reachability (= round(100 × p_cat)) | Punt Score (= f(1 − p_cat) + volatility) | Verdict |
63|---|---|---|---|---|---|
64| R | losing | 72 | 36 | 44 | push (contested) |
65| HR | winning | 48 | 65 | 21 | maintain |
66| RBI | winning (thin) | 65 | 42 | 35 | push |
67| SB | losing | 55 | 16 | 58 | evaluate punt |
68| OBP | winning (thin) | 70 | 60 | 24 | push |
69| K | winning | 55 | 74 | 16 | push |
70| ERA | winning | 62 | 62 | 23 | push |
71| WHIP | winning | 60 | 68 | 19 | maintain |
72| QS | winning | 78 | 85 | 9 | push hard |
73| SV | losing | 38 | 10 | 84 | punt |
74
75**Overall recommendation**: **Push 6, maintain 2, punt 2.** Matchup win prob 58% (neutral favorite).
76
77- **Push (6)**: HR, OBP, K, ERA, QS — each has `per_cat_win_probability ≥ 0.60`. Plus RBI as the contested-but-reachable 6th.
78- **Maintain (2)**: WHIP (locked-ish), R (reachability lowish but not a true punt).
79- **Punt (2)**: SB (p = 0.16, low reach) and SV (p = 0.10 + volatility bonus → punt score 84).
80
81**Downstream implications for other agents**:
82- Lineup optimizer: `matchup_win_probability = 0.58` → neutral-to-favorite, standard daily_quality optimization (no variance tilt).
83- Waiver analyst: prioritize SP (QS, K, ERA), OBP-heavy bats; not closers or speed specialists.
84- Streaming strategist: every QS-capable SP starts; skip any 5-inning risk arm.
85
86## Workflow
87
88Copy this checklist and track progress:
89
90```
91MLB Category State Analysis Progress:
92- [ ] Step 1: Pull current matchup scores from Yahoo
93- [ ] Step 2: Count remaining games/PAs/IP for both rosters
94- [ ] Step 3: Build per-cat projection dicts ({mean, stddev}) for both rosters
95- [ ] Step 4: Delegate to matchup-win-probability-sim (pass cat_list, projections, threshold=6, inverse=[ERA,WHIP])
96- [ ] Step 5: Derive cat_position (from state), cat_pressure, cat_reachability, cat_punt_score from sim output
97- [ ] Step 6: Rank cats and emit push/maintain/punt plan
98- [ ] Step 7: Write signal file with YAML frontmatter (include matchup_win_probability from sim)
99```
100
101**Step 1: Pull current matchup scores**
102
103Web-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.
104
105- [ ] 5 batting cats: R, HR, RBI, SB, OBP (+ at-bats / plate-appearances)
106- [ ] 5 pitching cats: K, ERA, WHIP, QS, SV (+ innings pitched)
107- [ ] Source URL cited in signal file
108
109See [resources/methodology.md](resources/methodology.md#pulling-matchup-data-from-yahoo) for scrape procedure and fallback if Yahoo is unreachable.
110
111**Step 2: Count remaining games/PAs/IP**
112
113For 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).
114
115- [ ] Hitter games remaining: sum of (each rostered hitter's team games × probability they start)
116- [ ] Pitcher starts remaining: number of scheduled SP starts for the rest of the week per roster
117- [ ] Reliever days remaining: days × eligible RPs (for SV projection)
118- [ ] 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`)
119
120Use MLB.com schedules + probable pitcher grids. See [resources/methodology.md](resources/methodology.md#projecting-remaining-games).
121
122**Step 3: Build per-cat projection dicts**
123
124For 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.
125
126- [ ] **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.
127- [ ] **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.
128- [ ] Both dicts have identical keys and the exact league `cat_list`.
129- [ ] Use OBP (not AVG) and `qs_probability` (not W) from upstream `mlb-player-analyzer` signals — see Guardrails.
130
131See [resources/methodology.md](resources/methodology.md#building-per-cat-projection-dicts).
132
133**Step 4: Delegate to `matchup-win-probability-sim`**
134
135Invoke the sibling skill with a well-formed input payload:
136
137```
138inputs to matchup-win-probability-sim:
139 cat_list: [R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV]
140 cat_inverse_list: [ERA, WHIP]
141 cat_win_threshold: 6
142 our_per_cat_projection: <dict from Step 3>
143 opp_per_cat_projection: <dict from Step 3>
144 sim_mode: "monte_carlo"
145 n_simulations: 10000
146 random_seed: 42
147 tie_rule: "half"
148
149outputs consumed:
150 matchup_win_probability (float in [0,1])
151 per_cat_win_probability (dict[cat, float])
152 expected_cats_won (float)
153 variance_estimate (float)
154```
155
156- [ ] All 10 cats present in both projection dicts
157- [ ] `cat_inverse_list = [ERA, WHIP]` (lower-is-better)
158- [ ] `cat_win_threshold = 6` (Yahoo 10-cat majority)
159- [ ] Seed passed for reproducibility
160- [ ] Sim output fields captured and stored for Step 5
161
162**Step 5: Derive per-cat signals from sim output + state**
163
164Apply the formulas in [resources/methodology.md](resources/methodology.md#signal-formulas). The sim owns the probability math; this skill owns the baseball-state interpretation.
165
166- [ ] `cat_position` ∈ {winning, tied, losing} — computed locally from **current totals** (not sim). Ratio-cat direction handled (OBP higher = winning; ERA/WHIP lower = winning).
167- [ ] `cat_pressure` (0–100) — simple arithmetic from position + close-margin + volume-edge + locked-in flags. See pressure formula in Quick Reference.
168- [ ] `cat_reachability` (0–100) — **now = round(100 × per_cat_win_probability[cat])**, taken directly from the sim.
169- [ ] `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.
170
171**Step 6: Rank and emit plan**
172
173Rank all 10 cats by `cat_pressure × cat_reachability / 100`:
174
175- [ ] **Top 6**: push — mark these as priority for waivers, streams, starts
176- [ ] **Middle 2**: maintain — hold position, don't overspend
177- [ ] **Bottom 2**: evaluate punt — if `cat_punt_score > 60`, confirm punt; otherwise hold
178
179Goal in H2H Cats is 6-of-10. A defensible plan is "push 6, concede up to 4." See [resources/template.md](resources/template.md#per-cat-signal-table) for the output signal format.
180
181**Step 7: Write signal file**
182
183Write 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.
184
185- [ ] All 10 cats present with all 4 signals each
186- [ ] `matchup_win_probability` and `expected_cats_won` recorded in frontmatter
187- [ ] `sim_meta` block (sim_mode, n_simulations, random_seed) recorded for reproducibility
188- [ ] Confidence reflects data quality (lower if Yahoo scrape was partial)
189- [ ] `source_urls` includes Yahoo matchup page + MLB.com schedule pages + a reference to the sim skill
190- [ ] Red-team findings noted (e.g., "Opp has a two-start ace coming that could flip K + ERA + WHIP all at once")
191
192Validate output using [resources/evaluators/rubric_mlb_category_state_analyzer.json](resources/evaluators/rubric_mlb_category_state_analyzer.json). Minimum: average score of 3.5 or above.
193
194## Common Patterns
195
196**Pattern 1: Balanced mid-week state**
197- Typical Wednesday AM state: 3-4 cats already locked, 3-4 close, 2-3 volatile.
198- Action: push the close cats hardest, coast the locked wins, ignore locked losses.
199- The sim's per-cat probs already reflect this — cats with `p ∈ [0.40, 0.65]` are the contested ones.
200
201**Pattern 2: Volume-imbalanced matchup**
202- 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.
203- 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.
204
205**Pattern 3: Two-start ace incoming (us or them)**
206- One pitcher's two-start week can swing K, ERA, WHIP, QS simultaneously.
207- 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.
208- The sim then shows 4 pitching cats moving together in `per_cat_win_probability` deltas.
209
210**Pattern 4: Save-category volatility**
211- SVs are low-frequency; one walkoff blown save flips the category.
212- 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+.
213- The +30 volatility bonus in `cat_punt_score` (applied here, not in the sim) pushes SV to punt when sim reachability agrees.
214
215**Pattern 5: Ratio-cat "freeze"**
216- 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.
217- 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.
218
219## Guardrails
220
2211. **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.
222
2232. **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.
224
2253. **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.
226
2274. **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.
228
2295. **`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.
230
2316. **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.
232
2337. **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`.
234
2358. **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.
236
2379. **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`.
238
239## Quick Reference
240
241**Where the math lives now:**
242
243| Signal | Owner | Formula |
244|---|---|---|
245| `cat_position` | this skill | enum from current totals (ratio-direction aware) |
246| `cat_pressure` | this skill | baseline 50 + 20 × close + 15 × vol-edge − 10 × locked_win − 30 × locked_loss |
247| `cat_reachability` | **delegated to `matchup-win-probability-sim`** | = round(100 × `per_cat_win_probability[cat]`) |
248| `cat_punt_score` | this skill (uses sim output) | (100 × (1 − p_cat)) × 0.6 + 30 × volatile + 20 × below_min − 10 × spillover |
249| `matchup_win_probability` | **delegated to `matchup-win-probability-sim`** | Monte Carlo P(cats_won ≥ 6) |
250
251```
252cat_pressure =
253 50 # neutral baseline
254 + 20 × (is_close_margin: deficit/lead ≤ 10% of total)
255 + 15 × (opponent_volume_exhausted: we have more games left)
256 - 10 × (locked_in_win)
257 - 30 × (locked_in_loss)
258 clamp(0, 100)
259
260cat_reachability = round(100 × per_cat_win_probability[cat]) # from sim
261
262cat_punt_score =
263 (100 - cat_reachability) × 0.6 # base: if we can't reach, consider punting
264 + 30 × (cat is traditionally volatile: SV)
265 + 20 × (below min-PA/IP threshold)
266 - 10 × (cat has spillover: K→QS, OBP→R, HR→R+RBI)
267 clamp(0, 100)
268```
269
270**League constants (from `context/league-config.md`):**
271
272- 10 cats: **R, HR, RBI, SB, OBP, K, ERA, WHIP, QS, SV**
273- Inverse cats: **ERA, WHIP** (lower-is-better — passed as `cat_inverse_list` to the sim)
274- OBP (not AVG) — walks matter
275- QS (not W) — 6+ IP with ≤3 ER
276- H2H Cats, goal = win 6+ of 10 each week (`cat_win_threshold = 6`)
277- Daily lineup lock; weekly matchup rolls Mon-Sun
278
279**Signal file output schema (from `context/frameworks/signal-framework.md`):**
280
281```yaml
282---
283type: cat-state
284date: YYYY-MM-DD
285emitted_by: mlb-category-state-analyzer
286week: N
287matchup_opponent: <team name>
288scoring_days_remaining: N
289matchup_win_probability: 0.58 # from matchup-win-probability-sim
290expected_cats_won: 5.18 # from matchup-win-probability-sim
291sim_meta:
292 sim_mode: monte_carlo
293 n_simulations: 10000
294 random_seed: 42
295synthesis_confidence: 0.0-1.0
296source_urls:
297 - https://baseball.fantasysports.yahoo.com/b1/23756/5/matchup?week=N
298---
299```
300
301Body: per-cat table + overall push/maintain/punt recommendation + red-team findings.
302
303**Thresholds used downstream:**
304
305| Agent | Threshold | Effect |
306|---|---|---|
307| Waiver analyst | `cat_pressure ≥ 60` | Prioritize targets that fill that cat |
308| Streaming strategist | `cat_pressure (ERA/WHIP) < 30` | Allow riskier streamers (we're punting) |
309| Lineup optimizer | `matchup_win_probability < 0.4` / `> 0.6` | Variance-seek as underdog / damp as favorite |
310| Trade analyzer | weights `trade_cat_delta` | Multiplied by `cat_pressure / 50` |
311
312**Key resources:**
313
314- **[resources/template.md](resources/template.md)**: Output signal file format, per-cat table, sim-integration worked example
315- **[resources/methodology.md](resources/methodology.md)**: Yahoo scrape procedure, remaining-games projection, building per-cat projection dicts, sim-integration formulas
316- **[resources/evaluators/rubric_mlb_category_state_analyzer.json](resources/evaluators/rubric_mlb_category_state_analyzer.json)**: Evaluator rubric
317- **Sibling skill: `matchup-win-probability-sim`** — owns per-cat and matchup-level win-probability math via Monte Carlo / Poisson-binomial
318
319**Inputs required:**
320
321- Current matchup scores (10 cats, both teams, with volume for ratio cats)
322- Roster IDs for both teams
323- Remaining MLB schedule through Sunday
324- Upstream signals: `qs_probability`, `save_role_certainty`, `obp_contribution`, `sb_opportunity`, `daily_quality`
325- League config (cats list, min-IP/PA thresholds, `cat_win_threshold`)
326
327**Outputs produced:**
328
329- `signals/YYYY-MM-DD-cat-state.md` — signal file with 10-cat table, overall plan, `matchup_win_probability`, confidence, source URLs
330- `cat_position`, `cat_pressure`, `cat_reachability`, `cat_punt_score` per cat
331- Overall "push N, maintain M, punt P" recommendation (N + M + P = 10, target N ≥ 6)
332- `matchup_win_probability` (from sim delegate) for lineup-optimizer variance decisions