skills/mlb-category-state-analyzer/SKILL.md
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.
npx skillsauth add lyndonkl/claude mlb-category-state-analyzerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
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 = 6our_per_cat_projection / opp_per_cat_projection from the table abovesim_mode = "monte_carlo", n_simulations = 10000, random_seed = 42Sim output (consumed by this skill):
matchup_win_probability = 0.58per_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.10expected_cats_won = 5.18Per-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).
per_cat_win_probability ≥ 0.60. Plus RBI as the contested-but-reachable 6th.Downstream implications for other agents:
matchup_win_probability = 0.58 → neutral-to-favorite, standard daily_quality optimization (no variance tilt).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).
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.
mean = current_total + Σ(per-player per-game rate × games remaining × daily_quality). stddev ≈ 0.35 × expected_remaining as a default CV.mean = (current_ratio × current_volume + projected_remaining_ratio × remaining_volume) / total_volume. stddev ≈ σ_per_obs / sqrt(total_volume) — shrinks as total IP/PA grows.cat_list.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)
cat_inverse_list = [ERA, WHIP] (lower-is-better)cat_win_threshold = 6 (Yahoo 10-cat majority)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:
cat_punt_score > 60, confirm punt; otherwise holdGoal 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.
matchup_win_probability and expected_cats_won recorded in frontmattersim_meta block (sim_mode, n_simulations, random_seed) recorded for reproducibilitysource_urls includes Yahoo matchup page + MLB.com schedule pages + a reference to the sim skillValidate output using resources/evaluators/rubric_mlb_category_state_analyzer.json. Minimum: average score of 3.5 or above.
Pattern 1: Balanced mid-week state
p ∈ [0.40, 0.65] are the contested ones.Pattern 2: Volume-imbalanced matchup
per_cat_win_probability for R/HR/RBI/SB rises accordingly.Pattern 3: Two-start ace incoming (us or them)
per_cat_win_probability deltas.Pattern 4: Save-category volatility
per_cat_win_probability near 0.1–0.25 when behind by 2+.cat_punt_score (applied here, not in the sim) pushes SV to punt when sim reachability agrees.Pattern 5: Ratio-cat "freeze"
per_cat_win_probability ≈ 1.0 for those cats.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.
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):
cat_inverse_list to the sim)cat_win_threshold = 6)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:
matchup-win-probability-sim — owns per-cat and matchup-level win-probability math via Monte Carlo / Poisson-binomialInputs required:
qs_probability, save_role_certainty, obp_contribution, sb_opportunity, daily_qualitycat_win_threshold)Outputs produced:
signals/YYYY-MM-DD-cat-state.md — signal file with 10-cat table, overall plan, matchup_win_probability, confidence, source URLscat_position, cat_pressure, cat_reachability, cat_punt_score per catmatchup_win_probability (from sim delegate) for lineup-optimizer variance decisionsdevelopment
--- name: zettel-note description: The note-writing discipline for this vault's evergreen knowledge graph, modeled on a Zettelkasten reading companion and governed by the vault conventions. Enforces declarative-claim titles, one claim per note (atomicity), own-words prose with no block quotes, the piped [[slug|Title]] link form, the labeled link-relationship vocabulary (Confirms/Contradicts/Extends/Context/Prerequisite/Builds-on/Applies/Example-of/Contrasts-with), 3-6 links per note, and search-
development
Plans between-round FIFA World Cup Fantasy transfers — budgets the round's free transfer(s), forces out players whose nation has been eliminated, chases fixture-swing drops, upgrades on value, and decides when a rebuild is large enough to fire the Wildcard instead of spending free transfers one at a time. Ranks candidate in/out pairs by EV gain over each player's remaining survival horizon (delta xEV weighted by progression_carry) MINUS transfer cost (a free transfer is cheap, a points hit is real, churning the squad for marginal swings is a critic flag), and tags forced/fixture/upgrade priority. Emits a `transfer-plan` signal. Use when called by wc-squad-architect (whose transfer work this skill is the engine for) and by the strategists in the populate stage when their candidate is transfer-adjacent rather than a full rebuild.
testing
Reads and updates the FIFA World Cup Fantasy tournament state machine (footballfantasy/context/tournament-state.md) — the temporal backbone tracking phase (pre-tournament → group MD1-3 → R32 → R16 → QF → SF → final), budget ($100m group / $105m knockouts), nation cap (3 group, loosening in knockouts), chips remaining, surviving nations, each owned player's elimination-risk horizon, and deadlines. Validates state on load (count/feasibility checks), applies phase transitions, and appends to the append-only state log (never silent overwrite). Use to load state at the start of a run and to commit state changes after the manager makes a move.
development
Validates and persists FIFA World Cup Fantasy signal files to signals/YYYY-MM-DD-<type>.md. Checks the required frontmatter (type, round, date, emitted_by, confidence, source_urls), range-checks declared numeric signals, confirms every factual claim carries a source URL or "manager-provided", rejects unknown signal types, and refuses to persist a signal that fails validation (logging the failure instead). Keeps the inter-agent signal layer auditable so downstream agents can trust what they read and never re-derive it. Use whenever an agent or skill writes a signal.