skills/mlb-trade-evaluator/SKILL.md
Computes the full impact of a proposed MLB fantasy trade across all 10 H2H categories (R/HR/RBI/SB/OBP, K/ERA/WHIP/QS/SV), rest-of-season dollar value, positional flexibility, slot-value optionality, adverse-selection prior, and weeks 21-23 playoff impact. Produces a signed verdict (accept / counter / reject) with rationale and a specific counter if applicable. Use when user mentions "trade evaluation", "trade value", "should I accept", "trade delta", "counter offer", or pastes in a trade proposal from Yahoo. Defaults to COUNTER in the middle band — pure REJECT is reserved for clearly predatory offers.
npx skillsauth add lyndonkl/claude mlb-trade-evaluatorInstall 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: Opponent offers Aaron Judge (OF) for Bobby Witt Jr. (SS) + Spencer Strider (SP). Today is 2026-06-10. User is 4th in 12-team league. cat_pressure signals from mlb-category-state-analyzer: SB pressure 85, HR pressure 55, K pressure 70, QS pressure 80, OBP pressure 60; others 40-50. User has spare OF depth but thin SS. Opponent archetype from mlb-opponent-profiler: active.
Rest-of-season ATC projections (weeks 11-23, ~105 games remaining):
| Player | R | HR | RBI | SB | OBP | K | QS | SV | $ value | |---|---|---|---|---|---|---|---|---|---| | Judge (incoming) | 75 | 28 | 78 | 2 | .405 | — | — | — | $34 | | Witt (outgoing) | 70 | 20 | 65 | 25 | .355 | — | — | — | $32 | | Strider (outgoing) | — | — | — | — | — | 165 | 12 | 0 | $26 |
Per-category delta (ours minus theirs, weighted by cat_pressure):
| Cat | Delta (raw) | Pressure | Weighted Δ | |---|---|---|---| | R | +5 | 45 | +2.3 | | HR | +8 | 55 | +4.4 | | RBI | +13 | 45 | +5.9 | | SB | −25 | 85 | −21.3 | | OBP | +.050 (×PA) | 60 | +3.0 | | K | −165 | 70 | −115.5 | | QS | −12 | 80 | −96.0 | | SV | 0 | 45 | 0 | | ERA/WHIP | neutral | 50 | 0 | | Sum | | | −217.2 |
Raw trade value delta = $34 − ($32 + $26) = −$24.
Slot-value bonus (principle #9): 2 players out, 1 player in → we clear +1 net slot; they clear 0. slot_value_delta = (1 − 0) × $2.50 = +$2.50.
Adverse-selection pipeline (delegated to @skills/adverse-selection-prior/):
offer_type=trade, proposer_archetype=active, offer_symmetry_score=20 (pre-adj ratio −37%, clearly lopsided against us), proposer_info_asymmetry=55.prior_ev_probability=0.30, recommended_adjustment=0.85, rationale cites active trader selecting this offer from many possibilities despite surface lopsidedness against us.Adjusted trade value delta:
trade_value_delta_raw = −$24.00
+ slot_value_delta = +$2.50
= trade_value_delta_pre_adj = −$21.50
× recommended_adjustment = × 0.85
= trade_value_delta_adjusted= −$18.28
Express as % of outgoing dollars: −$18.28 / $58 = −31.5% → below the −20% REJECT threshold.
Positional flex delta: −30 (lose a scarce SS, gain a surplus OF). Playoff impact (weeks 21-23): Judge's schedule has 19 games vs Witt's 18 + Strider's 4 starts → −35 (lose 4 starts of Ks/QS during championship weeks).
Verdict ladder (principle #8):
delta_pct = −31.5%, clearly below −20%.delta < −20% AND clear adverse-selection evidence.Verdict: REJECT. Even the always-counter rule yields: we include the counter we would have sent — "Judge + Webb + Ruiz for Witt + Strider" — as the walk-away ask, but pre-commit to reject because the haircut keeps it underwater.
Copy this checklist and track progress:
Trade Evaluation Progress:
- [ ] Step 1: Parse the offer (players in, players out, both sides)
- [ ] Step 2: Pull rest-of-season ATC projections for every player
- [ ] Step 3: Compute per-category deltas (counting + ratio)
- [ ] Step 4: Apply cat_pressure weights from category-state-analyzer
- [ ] Step 5: Compute raw trade_value_delta in dollars
- [ ] Step 6: Compute slot_value_delta (optionality bonus)
- [ ] Step 7: Invoke @skills/adverse-selection-prior/ and apply the haircut
- [ ] Step 8: Assess positional_flex_delta
- [ ] Step 9: Compute playoff_impact (weeks 21-23 only, July+)
- [ ] Step 10: Render verdict + counter via the +15% / -20% ladder
Step 1: Parse the offer
Record exactly who is being given up and who is being received, on both sides. Trades are zero-sum within the league. See resources/template.md for the input schema.
context/opponents/<team>.md for their archetypeStep 2: Pull rest-of-season projections
The authoritative source is FanGraphs ATC (ensemble projection). Fallback: FanGraphs Depth Charts, then Razzball Player Rater. For every player involved, get projected totals for remaining season.
See resources/methodology.md#projection-sourcing for detailed source order.
Step 3: Compute per-category deltas
For counting stats (R, HR, RBI, SB, K, QS, SV): simple sum of incoming minus outgoing.
For ratio stats (OBP, ERA, WHIP): weight by projected PAs (batters) or IP (pitchers). See resources/methodology.md#ratio-category-math for the weighting formula.
Step 4: Apply cat_pressure weights
Read the most recent signals/YYYY-MM-DD-cat-state.md from mlb-category-state-analyzer. For each of the 10 cats, multiply the raw delta by cat_pressure / 50.
cat_pressure_weighted_deltaStep 5: Compute raw trade_value_delta
Use FanGraphs Auction Calculator rest-of-season dollar values (or Razzball as fallback).
trade_value_delta_raw = Sum($ of players IN) − Sum($ of players OUT)
This is the starting point — it does NOT yet include the slot-value bonus or the adverse-selection haircut.
Step 6: Compute slot_value_delta (optionality bonus)
Every open bench slot is worth ~$2-3 in optionality: it can host a streamer, an IL stash, or a handcuff speculation. A 2-for-1 trade frees a roster slot on one side.
slot_value_delta = (N_slots_cleared_for_us − N_slots_cleared_for_them) × $2.50
Where:
N_slots_cleared_for_us = (players OUT from our side) − (players IN to our side), clamped at ≥ 0N_slots_cleared_for_them = mirror on their sideExamples:
slot_value_delta = $0Add to trade_value_delta_raw to form trade_value_delta_pre_adj.
See resources/methodology.md#slot-value-optionality for the rationale (game-theory-principles.md #9).
Step 7: Invoke @skills/adverse-selection-prior/ and apply the haircut
This step operationalizes principle #4 (adverse selection on incoming offers). The evaluator does NOT compute the prior itself — it delegates to the dedicated skill and consumes the output.
@skills/adverse-selection-prior/:
offer_type: trade (or counter_offer if this is a counter to a prior proposal)proposer_archetype: read from context/opponents/<opponent-team>.md (one of active, expert, dormant, frustrated, unknown). If file missing, use unknown.offer_symmetry_score: integer 0-100 derived from our own surface valuation. Map via:
trade_value_delta_pre_adj / Σ($_OUT) ≥ 0 → 72 (offer looks fair-to-favorable)proposer_info_asymmetry: integer 0-100. Default 50. Raise toward 80+ if any of: recent injury news we have not yet verified, closer role shift pending, lineup construction change, trade-deadline timing. See resources/methodology.md#adverse-selection-delegation for the scoring guide.@skills/adverse-selection-prior/ with those four inputs.prior_ev_probability — store verbatim in the signal file.recommended_adjustment (float, typically 0.80-1.00) — this is the multiplicative haircut.bayesian_rationale — embed in the verdict block's rationale.override_hints — add each hint as a red_team_finding in the signal file.trade_value_delta_adjusted = trade_value_delta_pre_adj × recommended_adjustment
adverse_selection_adjustment in the output signal block = 1 − recommended_adjustment (expressed as a percentage haircut, e.g., 0.88 → "12% haircut").Sign note: The haircut is multiplicative on the delta. It shrinks the MAGNITUDE of our estimate in both directions — a positive delta moves toward zero (we over-estimated the gain), and a negative delta also moves toward zero (we over-estimated the loss). This is mathematically correct for an Akerlof-style prior: the skill tells us our model is less reliable than we thought, so we should anchor more toward zero. The verdict ladder compensates by setting tight thresholds (+15% / −20%) on the post-haircut percentage, so a moderately negative raw delta with a deep haircut will land in the middle band (COUNTER), while a very negative raw delta with any haircut will still clear the −20% REJECT threshold. If the haircut creates a tie at a ladder boundary, resolve toward COUNTER (per the always-counter rule).
Step 8: Assess positional_flex_delta
Score −100 to +100 based on how the trade changes roster flexibility. See resources/methodology.md#positional-flex-scoring.
Step 9: Compute playoff_impact (July 1+ only)
For trades made July 1 or later, evaluate specifically for championship weeks 21, 22, 23. Read the mlb-playoff-scheduler signal file for playoff_games and playoff_matchup_quality per player.
playoff_impact = Sum(playoff_games × matchup_quality of IN)
− Sum(playoff_games × matchup_quality of OUT)
Normalize to 0-100 where 50 = neutral.
Step 10: Render verdict via the +15% / −20% ladder
Express trade_value_delta_adjusted as a percent of outgoing dollars:
delta_pct = trade_value_delta_adjusted / Σ($_OUT)
Apply the principle #8 verdict ladder:
| delta_pct | Verdict | Conditions |
|---|---|---|
| ≥ +15% | ACCEPT | AND advocate/critic variants agree AND no cat with pressure ≥80 has negative weighted delta |
| −20% < delta_pct < +15% | COUNTER (with specific package) | ALWAYS produce a specific counter; never pure-reject in this band |
| ≤ −20% | REJECT | AND prior_ev_probability ≤ 0.35 (clear adverse-selection evidence) |
Always-counter rule: in the middle band, even if our instinct is to decline, we ship a specific counter-package per principle #8 (repeated-game reputation). Rejection dismissively dries up future offers; a reasoned counter keeps the pipeline open.
If REJECT conditions are only partially met (e.g., delta_pct ≤ −20% but prior_ev_probability > 0.35): downgrade to COUNTER with a more demanding package.
Every verdict ends with a single-verb recommendation: ACCEPT, COUNTER (with specific proposal), or REJECT. No "consider."
See resources/template.md#verdict-block for output schema. Validate using resources/evaluators/rubric_mlb_trade_evaluator.json. Minimum average score to ship: 3.5.
Pattern 1: Star-for-depth offer
slot_value_delta tilts toward us (+$2.50 or +$5); but the adverse-selection haircut applies — check if trade_value_delta_adjusted clears +15% AND the players we give up aren't droppable-tier. If in doubt, COUNTER (not REJECT).Pattern 2: Category-targeted swap
cat_pressure, or does it match THEIRS? If theirs, haircut is deep (symmetry actually bad for us).delta_pct ≥ +15% after haircut. Otherwise COUNTER.Pattern 3: Buy-low / sell-high regression play
mlb-regression-flagger signal.regression_index is sharply positive (unlucky, bounce-back due) AND ours is sharply negative (lucky, regression incoming), pass this to @skills/adverse-selection-prior/ as proposer_info_asymmetry of 30-40 (we plausibly have better regression info than they do). The resulting shallow haircut may let delta_pct clear +15%.Pattern 4: Injury-adjacent desperation offer
proposer_info_asymmetry = 80+ (they know more about the injury). The skill will return recommended_adjustment ≈ 0.80. Usually COUNTER or REJECT depending on whether delta_pct after haircut lands below −20%.Pattern 5: Playoff-week schedule arbitrage (July+)
playoff_impact for any trade proposed July 15 or later. Use playoff_impact positive swings to tip close COUNTER cases into ACCEPT.Always counter — pure REJECT is narrow. Per game-theory-principles.md #8 (repeated-game reputation), rejecting fairly with a counter keeps offer pipelines open; dismissive rejection dries them up. REJECT is reserved for delta_pct ≤ −20% AND clear adverse-selection evidence (prior_ev_probability ≤ 0.35). Every other middle-band offer → COUNTER with a specific package.
Delegate the adverse-selection prior — never recompute it inline. Step 7 invokes @skills/adverse-selection-prior/. Do not duplicate its logic here. The skill is reused across trades, waiver drops, and future M&A/negotiation skills; keeping it single-source prevents drift.
Apply the haircut to trade_value_delta_pre_adj, not to per-category deltas. The prior adjusts our net dollar estimate, not individual cat contributions. The cat delta table stays un-haircut and is displayed as-is in the template.
Slot-value bonus is symmetric. If they send two and we send one, slot_value_delta is NEGATIVE from our perspective — they got the bench-slot optionality. Do not forget the minus sign on incoming 2-for-1s.
Use rest-of-season projections, never full-season or season-to-date. Same rule as before; atcr not atc.
Ratio categories need volume weighting. OBP, ERA, WHIP deltas must be PA- or IP-weighted.
Read the cat_pressure signal first. If mlb-category-state-analyzer has not emitted for today, run it first.
Quantify the counter — don't say "ask for more." Propose a specific alternative package with named players.
Check for trade deadline proximity. Yahoo's deadline is August 6. Flag any trade proposed in the week before.
Two-way impact. If the opponent is a direct playoff-seed competitor, the trade helping them is doubly bad. Note opponent identity and their standings proximity in the output block.
Beginner-voice output. The user has zero baseball knowledge. Translate every stat into plain English at least once. Every user-facing recommendation ends with ACCEPT, COUNTER (with specific package), or REJECT.
Log the decision. Emit via mlb-decision-logger to tracker/decisions-log.md with full signal values, including prior_ev_probability, recommended_adjustment, slot_value_delta, trade_value_delta_adjusted, verdict, confidence, and will_verify_on date (4 weeks out).
Key formulas:
Counting-cat delta (cat C):
raw_delta_C = Σ(projected_C of IN) − Σ(projected_C of OUT)
Ratio-cat delta (OBP example):
PA-weighted OBP_IN and OBP_OUT, then team-level shift
Weighted cat delta:
weighted_delta_C = raw_delta_C × (cat_pressure_C / 50)
Total cat delta:
trade_cat_delta = Σ weighted_delta_C
Raw trade value:
trade_value_delta_raw = Σ($_IN) − Σ($_OUT)
Slot-value bonus:
slot_value_delta = (slots_cleared_us − slots_cleared_them) × $2.50
Pre-adjustment delta:
trade_value_delta_pre_adj = trade_value_delta_raw + slot_value_delta
Adverse-selection haircut (via @skills/adverse-selection-prior/):
trade_value_delta_adjusted = trade_value_delta_pre_adj × recommended_adjustment
Percent expression:
delta_pct = trade_value_delta_adjusted / Σ($_OUT)
Playoff impact (July+):
playoff_impact = Σ(games_21_23 × matchup_q of IN)
− Σ(games_21_23 × matchup_q of OUT)
Verdict ladder (principle #8):
| delta_pct | Verdict | Additional gates |
|---|---|---|
| ≥ +15% | ACCEPT | AND advocate/critic agree AND no pressure ≥80 cat is negative |
| −20% < d < +15% | COUNTER (with specific package) | ALWAYS — never pure-reject in this band |
| ≤ −20% | REJECT | AND prior_ev_probability ≤ 0.35 |
Adverse-selection prior summary (from @skills/adverse-selection-prior/):
| recommended_adjustment | Typical cause | Effect on delta_pct |
|---|---|---|
| 1.00 | Dormant opponent, shallow info gap | No change |
| 0.95 | Active opponent, symmetric offer | −5% on magnitude |
| 0.90 | Standard active opponent | −10% on magnitude |
| 0.85 | Expert opponent, some info gap | −15% on magnitude |
| 0.80 | Expert opponent, material info asymmetry | −20% on magnitude |
Key resources:
Inputs required:
cat_pressure signal (from mlb-category-state-analyzer)context/opponents/<team>.md (written by mlb-opponent-profiler)regression_index signal (from mlb-regression-flagger) for buy-low/sell-high check and info-asymmetry scoringplayoff_games and playoff_matchup_quality per player@skills/adverse-selection-prior/ (Step 7)Outputs produced:
trade_cat_delta per each of 10 cats (raw + pressure-weighted)trade_value_delta_raw ($)slot_value_delta ($)trade_value_delta_pre_adj ($)prior_ev_probability (from delegated skill)recommended_adjustment (from delegated skill)adverse_selection_adjustment (= 1 − recommended_adjustment)trade_value_delta_adjusted ($)delta_pct (trade_value_delta_adjusted / Σ$_OUT)positional_flex_delta (±100)playoff_impact (0-100, July+)verdict (accept / counter / reject) with rationale (includes bayesian_rationale from adverse-selection skill)signals/YYYY-MM-DD-trade-<opponent>.mdmlb-decision-loggertesting
Cluster a conference's event records into a small set of coarse themes with finer sub-clusters, an explicit outlier bucket, and soft (multi-membership) affinities — using the hybrid embed-then-label pipeline (embed abstracts, reduce, density-cluster, then LLM-label the clusters) when embedding libraries are available, and an LLM-reasoned hierarchical fallback when they are not. Embeddings do the grouping; the LLM only names the groups. Conference-agnostic. Use when turning structured event records into a navigable theme map for preference elicitation and scheduling, when you need 6-8 reasonable themes rather than 20 muddy ones, or when overlapping talks must belong to more than one theme. Trigger keywords - theme clustering, cluster talks, embed then label, soft membership, outlier talks, conference themes, topic map.
development
Build a personal conference schedule as a constraint-optimization problem — hard constraints (no time overlap, room-to-room travel time, capacity/registration, the attendee's own must-attends and blackouts) plus a user-owned weighted objective trading interest against breadth, pacing (maximize contiguous free time), and serendipity. Surfaces unbreakable conflicts (two high-value overlapping talks the model cannot rank) as decisions for the human rather than silently picking, and reports what each choice traded away. Conference-agnostic. Use to turn a preference profile plus a theme map into a day-by-day plan, to resolve overlapping sessions, or to balance a packed vs paced schedule. Trigger keywords - schedule optimization, conference schedule, constraint optimization, overlapping talks, contiguous free time, conflict surfacing, packed vs paced.
development
Parse a heterogeneous conference program (markdown, HTML, PDF-derived text, or JSON) into normalized event records with per-field confidence scores and independent classification axes (topic, depth, format, prerequisites, recorded, capacity). Detects the program's format before extracting, treats every inferred field as uncertain (present vs inferred vs missing), and flags thin or missing abstracts so downstream enrichment can target them. Conference-agnostic. Use when ingesting a conference or event schedule into a structured store, normalizing a talk/session list, or extracting per-session metadata with calibrated confidence. Trigger keywords - program ingestion, parse schedule, session extraction, event records, conference program, talk metadata, per-field confidence.
development
Build a personalized preference profile from a small number of well-chosen, cluster-grounded questions instead of a long survey. Represents the person's interests as an uncertainty region over the theme map, picks the single highest-information-gain choice-based question (contrasting real talks from different clusters), balances exploiting known interests against exploring uncertain ones, deliberately injects outlier probes to fight selection bias, and stops as soon as the schedule would be stable. Also elicits the user-owned objective weights and hard constraints. Interactive — runs where it can actually ask the person. Conference-agnostic. Use to turn a theme map into a preference profile, to decide what to ask a conference attendee, or to elicit scheduling priorities. Trigger keywords - preference elicitation, ask few questions, information gain, choice-based questions, selection bias probe, objective weights, attendee preferences.