Session 44: Make it visible — VYNDR archetype names, grade intel, schedule fix, landing page (2061 tests)
Frontend + wiring only. Wires existing backend into the pages users see. - VYNDR Original archetype rename (41) across archetypeService.js + lib/ archetypes.js + ArchetypeBadge, each keeping legacyName (resolves stale data). Judge -> BOMBER. Old POWER PULL slot -> WHIFF strikeout-artist pitcher. - BACKEND_HANDOFF.md: canonical frontend<->backend data contract. - Grade card intel: scan/page.tsx now forwards the engine's intel fields (season_avg/form/usage/matchup_grade/archetype/...) into mapScanToGradeResult -> STAT CONTEXT + VYNDR INTELLIGENCE sections populate. The chain already preserved them (tierGating + /api/scan spread); the page was dropping them. - Schedule freshness: slateAdapter.isRelevantGame drops completed games >24h old; Slate.filteredGames applies it. (TTL already 60s.) - Landing: Features.tsx rewritten to user-facing copy (no Point-biserial/Zone 14/ABS/Phi-coefficient). - Depth chart Next proxies added (/api/stats/lineup|depth|cascade) - were 404. - GameCard swap DEFERRED (Kev): legacy on-demand card stays as a bridge until the snapshot pipeline populates the grades cache; vyndr/GameCard swaps in then. Backend 2045 -> 2061 tests (+16), 167 suites. Web build clean (exit 0). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# VYNDR — Backend / Frontend Data Contract (BACKEND_HANDOFF.md)
|
||||
|
||||
**Canonical frontend↔backend data contract for the Player Intelligence System.**
|
||||
Every future session references this. Authored Session 44 from the shipped
|
||||
implementation (Sessions 42–44). When an endpoint or component shape changes,
|
||||
update this file in the same commit.
|
||||
|
||||
> Naming: archetypes are **VYNDR Originals** (TORCH, BOMBER, ALPHA, …). The old
|
||||
> descriptive labels live on each archetype as `legacyName` and are NEVER shown.
|
||||
|
||||
---
|
||||
|
||||
## 1. Archetypes (`src/services/archetypeService.js`)
|
||||
|
||||
`classify(sport, stats) → { sport, primary, secondary|null, blend }`
|
||||
|
||||
```
|
||||
primary | secondary : {
|
||||
name, legacyName, tag, sport, color (#hex), glyph (key),
|
||||
description, propDNA: { reliable: string[], volatile: string[] }, education
|
||||
}
|
||||
blend : [{ archetype: <NAME>, weight: 0..1 }] // normalized, top 4, sums ~1
|
||||
```
|
||||
|
||||
**Roster (41):** see `ARCHETYPES` registry. Colors are unique *within* a sport,
|
||||
reused *across* sports. Frontend visual map (color/glyph/desc) is mirrored in
|
||||
`web/src/lib/archetypes.js` — keep colors identical (a test enforces it).
|
||||
|
||||
- **NBA (15):** TORCH, CONDUCTOR, FORTRESS, ARTILLERY, SURGE, DUAL THREAT,
|
||||
CONNECTOR, FASTBREAK, PAINT BOSS, LOCKDOWN, SWITCHBOARD, ARCHITECT, PISTON,
|
||||
SENTINEL, IGNITER
|
||||
- **WNBA-unique (5):** DISTRIBUTOR, SHIELD, RANGE, SPARK, ANCHOR (plus reused NBA)
|
||||
- **MLB (15):** BOMBER, BRUSH, DRIVER, ALPHA, WHIFF, GHOST, HYBRID, FLEX,
|
||||
WORKHORSE, CATALYST, MIRROR, HAMMER, SINKER, BRIDGE, SWITCH
|
||||
- **Soccer (6):** FINISHER, MAESTRO, TOWER, MOTOR, BLADE, WALL
|
||||
|
||||
Classifier input shapes:
|
||||
- NBA/WNBA: `{ ppg, rpg, apg, bpg, spg, threes, usg, fg3a, pos, bench }`
|
||||
- MLB hitter: `{ avg, hr, rbi, sb, ops, runs, k_rate, doubles }`
|
||||
- MLB pitcher: `{ era, k9, whip, ip_per_start, saves, role: 'SP'|'RP'|'CL' }`
|
||||
|
||||
---
|
||||
|
||||
## 2. Stats API (`src/routes/stats.js`, mounted at `/api/stats`)
|
||||
|
||||
All player-intelligence endpoints are public, rate-limited 60/min, and ALWAYS
|
||||
return 200 with a valid (possibly empty) shape — the UI must never hard-fail.
|
||||
Browser must hit the **Next proxy** under `web/src/app/api/stats/...`, never
|
||||
Express directly.
|
||||
|
||||
### `GET /api/stats/player/:name?sport=nba`
|
||||
```
|
||||
{
|
||||
player, sport, team, found: boolean,
|
||||
archetype: { primary, secondary, blend }, // §1
|
||||
propDNA: { reliable, volatile },
|
||||
education: string,
|
||||
season: [{ k, v, lg? }], // display rows (mono)
|
||||
last10: [{ d, opp, res?, stat }],
|
||||
splits: [{ k, a, b }],
|
||||
gradeHistory: [{ grade, prop, hit?, miss? }],
|
||||
activeProps: [{ stat, line, side: 'O'|'U', grade, confidence }],
|
||||
intel: [{ label, kind: 'form'|'grade'|'plain', value, score?, color }],
|
||||
injury: { label, note, cascade } | null
|
||||
}
|
||||
```
|
||||
`found` is true when real season stats OR tonight's graded props exist. MLB stats
|
||||
come from `mlbStatsAdapter.getPlayerStats(name)`; NBA/WNBA from `nbaStatsClient`
|
||||
(degrades to `found:false` when the Python service is offline).
|
||||
|
||||
### `GET /api/stats/leaders?sport=mlb&stat=hits&limit=10`
|
||||
```
|
||||
{ sport, stat|null, leaders: [{ player, team, stat, line, side, grade, confidence }] }
|
||||
```
|
||||
Source: the `grades:{sport}` cache (tonight's graded slate, by confidence desc).
|
||||
|
||||
### `GET /api/stats/game/:id?sport=nba`
|
||||
ESPN game summary (injuries, leaders, ESPN Bet odds, box score) or `{error}`.
|
||||
|
||||
### `GET /api/stats/lineup/:team?sport=mlb` (Session 43)
|
||||
`{ sport, team, lineup: [{ player, position, battingOrder, projectedMinutes }] }`
|
||||
MLB returns the probable starting pitcher from the schedule.
|
||||
|
||||
### `GET /api/stats/depth/:team?sport=nba`
|
||||
`{ sport, team, positions: [{ position, starter, backup, thirdString }] }`
|
||||
|
||||
### `GET /api/stats/cascade/:player?sport=nba&team=SA`
|
||||
`{ sport, player, cascade: [{ player, stat, delta: '+x%', reason }] }`
|
||||
Usage redistribution weighted by archetype (SURGE benefits most).
|
||||
|
||||
---
|
||||
|
||||
## 3. Grade Result Card (`web/src/components/vyndr/GradeResultCard.tsx`)
|
||||
|
||||
`GradeResultData` (built by `web/src/lib/gradeAdapter.js` `mapScanToGradeResult`):
|
||||
core fields (player, team, sport, stat, line, side, grade, confidence, edge,
|
||||
projection, signals[], killConditions[], books[], altLadder[]) **plus** optional
|
||||
Player-Intelligence fields that self-hide when absent:
|
||||
```
|
||||
archetypeBlend?: [{ archetype, weight }]
|
||||
propDNA?: { reliable: string[], volatile: string[] }
|
||||
statContext?: { season?, last10?, vsOpp? }
|
||||
vyndrIntel?: { form?, usage?, matchup?, rest? }
|
||||
```
|
||||
The engine (`analyzeViaEngine1`) attaches snake_case fields
|
||||
(`season_avg`/`last10_avg`/`form`/`usage`/`matchup_grade`/`rest`, and when a
|
||||
season line is available, `archetype`/`archetype_blend`/`prop_dna`); the adapter
|
||||
maps them in. Archetype at grade time requires a multi-stat season line — until
|
||||
the snapshot pipeline supplies it, the archetype strip stays hidden.
|
||||
|
||||
---
|
||||
|
||||
## 4. Game Card (`web/src/components/vyndr/GameCard.tsx`)
|
||||
|
||||
`GameCardData` core (id, sport, away/home, time, venue, lines[], …) **plus**:
|
||||
```
|
||||
playerStrips?: [{ player, team, archetype?: {primary, secondary?}, stats: [{label,value}], props: [{stat,line,side,grade}] }]
|
||||
pitchers?: { away: {name, era, archetype?}, home: {name, era, archetype?} }
|
||||
```
|
||||
Built by `slateAdapter.mapScheduleToGameCards` (`groupPropsByPlayer` +
|
||||
`mapPitchers`). The card prefers `playerStrips` (name once, horizontal) over
|
||||
legacy per-prop rows. Book chips use `web/src/lib/books.js` brand colors.
|
||||
|
||||
---
|
||||
|
||||
## 5. Stat Strip (`web/src/components/vyndr/StatStrip.tsx`)
|
||||
|
||||
`compact` (game cards) | `expanded` (profile hero / grade result). HARD RULE: the
|
||||
player name appears ONCE; stats flow horizontally in JetBrains Mono. Props render
|
||||
inline with `GradeBadge`. `onPlayerClick` → `/player/:name?sport=` (`lib/playerHref.js`).
|
||||
|
||||
---
|
||||
|
||||
## 6. Freshness & caching
|
||||
|
||||
- Schedule cache TTL ≤ 30 min (`scheduleService`). Frontend filters completed
|
||||
games older than 24h out of the slate (`slateAdapter.isRelevantGame`).
|
||||
- Player props: PropLine primary (3-key rotation), The-Odds-API backup.
|
||||
- `grades:{sport}` cache (TTL 2h) is written by `gradeSlateService` on a fresh
|
||||
odds fetch; it feeds `/leaders` + the player card's `activeProps`.
|
||||
Reference in New Issue
Block a user