Version v1.38 · data dictionary
Spec + field map for the fair-AAV engine. End-to-end story: ARCHITECTURE.md · src/lib/pipeline/. Path: Cap Rink → this dictionary → misses. Also: research hub, thesis note, ex-ante. Storage: DATA.md.
Not an official CBA salary-cap sheet. Team payroll is season-roster AAV from curated/import contracts only — no Spotrac scrape, no LTIR / buried / retention / buyout math. Estimated placeholders are gaps, never silent filler in the total.
Is this player worth what they're paid — as a share of the cap, at today's ceiling and at the next-deal / FA-summer ceiling as the cap moves up? Contract AAVs are inputs. The product is worth (%cap) vs cost, with tier-matched market anchors as context when a clearing / elite path applies.
Same-season window: 2025-26 MoneyPuck production, NHL season rosters (API /roster/TEAM/20252026, not /current), and 2025-26 contract AAVs. Transparent TypeScript heuristics — not a trained Python model.
Fair AAV is "what would a reasonable mid-market deal look like for this production?" We blend a few seasons of rates, convert that into dollars, then compare to the actual paycheck in % of the salary capso older deals aren't punished for inflation.
elite-tier.all / 5on5 / 5on4 / 4on5 (+ playoffs when present) into a season impact bag.Names below are the ones you'll see in notes, code (SeasonImpact), and SQLite. MoneyPuck CSV column → DB column in parentheses.
| Model field | Source | Plain English |
|---|---|---|
| adjIxg | I_F_flurryScoreVenueAdjustedxGoals → adj_ix_goals | Preferred individual xG (flurry + score + venue adjusted) |
| ixg | I_F_xGoals → ix_goals | Raw individual xG fallback |
| hdIxg | I_F_highDangerxGoals → hd_ix_goals | High-danger individual xG |
| primaryPoints | I_F_goals + I_F_primaryAssists → primary_points | Goals + primary assists (offense that isn't secondary luck) |
| points | I_F_points → points | All points; secondary = pts − prim |
| icetimeSec / TOI | icetime → icetime_sec | Usage; drives D role ladder + F usage multiplier |
| relXgPct | onIce − offIce xGoals% (prefer 5on5) | Relative on-ice share vs team without them |
| onIceFxg / Axg | OnIce_F_xGoals / OnIce_A_xGoals | On-ice for/against xG → rates /60 |
| o/d ZoneStarts | I_F_*ZoneShiftStarts | Deployment tilt (OZ bump slightly discounts; DZ boosts) |
| ppIxg / pp·pk ice | situation 5on4 / 4on5 rows | Special-teams dollars |
| two-way bag | penalties±, takeaways, giveaways, dzone giveaways, blocks, FOW | Small dollar adjustments; FOW mainly for C |
adjxG82 / prim82— 82-game paced rates after GP pace dampening (<40 GP softens projection).TOI— minutes per game from blended icetime (with a points-based fallback if ice is empty so D role doesn't collapse).xWAR — proxy parts: offense + impact + special teams + two-way (xwar.ts). Not Evolving-Hockey GAR/WAR.GSAX — goalie xGA − GA (MoneyPuck), multi-year blended.estimated forces low confidence; import dampens high → medium.Stored on value_scores (fair/actual/surplus/label/confidence/notes). %cap fields are computed at score time (ValueResult) even when not every column is persisted.
legacy-%cap). Signed-future AAVs are shown as projection context, not current label burden. Stored cap_pct_at_sign remains for display.overpay | fair | underpay.low / medium / high from blended GP + seasons used; dampened for estimated AAV, ELC, sparse MP.3yr 50/30/20, riser 80/15/5, …), mkt-anchor, elite-tier, near-elite, live-%cap, legacy-%cap, ELC/bridge, comps …, season window, %cap actual→fair.ELC/bridge. Positive surplus usually stays fairunless talent clears a high bar vs a ~$4–5M bridge. Leaderboards hide AAV < $2.5M ("market board").skipMarketAnchor (ex-ante research).Player overview and contract pages show a short Why this deal block — makes sense / tough sledding / mixed — built by rules from the same Value Score inputs (label, surplusCapPct, at-sign vs live %cap residual, elite-tier / ELC notes, age × years remaining, sample confidence, prim/adjxG/xWAR). Not an LLM take; optional polish later would be labeled separately. Does not change fair AAV or labels.
Path: Cap Rink → player card (model inputs + thesis) → this section. Dual residuals on the card: at sign = fair% − %cap when signed; vs live = fair% − AAV ÷ today's ceiling. Scoring label uses the blended actual%cap (see Outputs).
Player pages lead with Worth — not primarily "fair $ vs current AAV." Single source: src/lib/value/worth.ts. Contract tab / UFA board / next-deal bands use the same helpers — no duplicate arithmetic in components. Not a METHOD_VERSION dial.
worthFromFairPct at production vs FA-summer ceiling (same fair%).anchors.ts (young_C / elite_wing / elite_F / top_D / starter_G). Worth cites matching tier only via anchorContextForTier: Carlsson/Bedard for young-C clearing; Kaprizov/Pasta/Rantanen for star-F; McDavid/Draisaitl/Matthews for elite-tier. Soft floors from tier medians / Kaprizov-class band (~14.8%) — not a hard peg. Mid-stars without a tier tag: no cite spam.fromCapPct); live $ via indexFairToLiveCap.burdenSchedule /burdenFromAav.burdenPct = AAV ÷ ceiling(season) (deal eases as the cap rises). Paths: rise (age ≤25 under-burden skaters — fair% grows toward a clamped role ceiling before decline; “aging up into deal”), hold (primes — flat fair%, cap dilution), decline (age ≥29 or clear age-curve drop — ageFactor). Growth clamps: ≤35% of the initial gap, ≤+2.5 pp, ≤1.22× current fair — no invented 1D miracles. First season where fair ≥ burden (or within 0.5 pp). Prefer signed_future gold when present. Projection only; ELC-scored fairs (e.g. Bedard) will show a long earn-by by design. See earnBy.ts.Assert: npm run test:worth.
AAV provenance is separate from Value Score confidence. Loud surplus / overpay theater only runs when the contract is PR-verified or curated. Estimated and unverified AAVs stay visible for books, but badges and headlines mute.
Team pages surface N/M PR-verified from coverage audits. Soft Cap projections never outrank thin trust. Source: src/lib/contracts/trust.ts.
A separate accounting layer — not a second Value Score and not a METHOD_VERSION dial. Team pages show committed AAV for the next two seasons; player contract tabs show “on books through …” for the live deal, any signed future gold extension, and a muted next-deal band only when ≤2 years remain and no extension is on file.
capHistory.ts; seasons beyond the last NHL/NHLPA figure step +8%/yr (recent raises ~5–9%). Each row is flagged known or projected.start_season / end_season covers the projection year. Future-start gold (Harley, Carlsson, etc.) stays off live 2025-26 AAV at resolve, but overlays into projections as signed_future once the season ≥ start — not as pending UFA/RFA.Still not CBA tooling: no LTIR, retention, burial, buyouts, or scraped rumor boards.
After the model scores, peers are matched on position group, scoring pace, age (±5), and deal size in %cap at signing (curated AAV ≥ $2M, 40+ GP, not ELC/bridge). Forwards prefer C↔C or wing↔wing when enough same-role peers exist.
blendWithCompspulls the stored result ~20–36% toward comps when n≥4 (0.20 / 0.28 / 0.36 at n≥4 / n≥6 / n≥8). ELC/bridge and AAV < $2M skip the post-pass.Feeling "off" is usually stale AAV data, ELC talent vs cost (correct but loud), or the dollar scale drifting vs the real market. Fix with a calibration loop — not mystery features.
npm run calibrate).Team pages list the NHL 2025-26 season roster only. Payroll is the sum of curated/import AAVs vs the $95.5M ceiling / $70.6M floor for context, plus surplus. Missing and estimated contracts are counted separately and excluded from the payroll total.
Player pages and Ask can answer "how should X look next year?" from the same DB: current vs prior MoneyPuck rates, TOI usage, the Value Score age curve, contract context, and an illustrative GP/points band if the role sticks. /research/outlook freezes those bands on past seasons. This is not a trained projection model — small samples stay low-confidence on purpose.
score.ts.Richer microstats, continuous contract feeds, and deeper ex-ante holdouts. Run npm run calibrate after every model change.