Session 16: Live hero prop, sport-specific markets fix, soccer weather, Sentry CSP (1429 tests)

This commit is contained in:
Kev
2026-06-11 18:15:25 -04:00
parent 167996d99a
commit 73b65a0248
11 changed files with 1010 additions and 101 deletions
+107 -1
View File
@@ -4,7 +4,113 @@
2026-06-11
## Current Phase
SHIP BUILD v15.0 — Intelligence hardening + platform correctness (Session 15)
SHIP BUILD v16.0 — Live hero prop + sport-scoped markets + launch polish (Session 16)
## Session 16 (2026-06-11) — SHIPPED
### Phase 1 — Sport-specific market map
`src/services/oddsService.js` now scopes the markets-list parameter
to the requested sport. Previously every odds-api request sent
`ALL_MARKETS` (the union of every sport's markets), which the
upstream 422'd on because soccer markets (`player_goals`,
`player_shots_on_target`, etc.) aren't valid for basketball
endpoints. Production briefly worked around this with a runtime
axios interceptor injected via
`NODE_OPTIONS=--require /app/data/patch.js`.
This session retires that hack at the code layer:
- New `SPORT_MARKETS` map alongside `SPORT_KEYS` — separate lists
per sport, all frozen with `Object.freeze`. NBA + NCAAB share
basketball markets; WNBA is basketball minus PRA (odds-api
doesn't carry that for WNBA); MLB sends batter + pitcher markets;
every soccer league shares the soccer set.
- `fetchEventOddsFromApi(sportKey, eventId, apiKey, sport)`
third arg added; reads `getMarketsForSport(sport)` instead of
the union. Backwards-compatible: omitted sport falls back to
NBA (safe default).
- `fetchAllOdds(sport, apiKey)` — already had the local sport key;
now passes it through.
**Coolify follow-up**: after this deploy, the operator can drop
`NODE_OPTIONS=--require /app/data/patch.js` from the web service
env and delete `/app/data/patch.js`. The runtime patch is now
dead code.
### Phase 2 — Live hero prop
`web/src/app/api/hero-prop/route.ts` (new) — picks one fresh real
prop from today's NBA → WNBA → MLB cascade and grades it. Two-stage
flow: GET `/api/odds/{sport}` → POST `/api/analyze/prop`. Both
calls share a 6s AbortController timeout. Server-side cached for
15 minutes via `Cache-Control: s-maxage=900`. Falls back to a
static Jokic example (`isStatic: true`) when every sport is empty
so the landing page never blanks out.
`web/src/components/LiveHeroProp.tsx` (new) — replaces the
hard-coded `FloatingDemoCard` inside `Hero.tsx`. Renders the live
prop with:
- "LIVE" badge with a pulsing green dot
- Sport-colored category tag (NBA red, WNBA orange, MLB blue, soccer green)
- Player name + line + projection + edge **visible** (hook)
- Grade letter + confidence **visible** via GradePill (proof)
- Reasoning section **blurred** with backdrop `blur(4px)`, a
scan-line gradient (`repeating-linear-gradient`), a bottom-fade
mask, and a "CLASSIFIED · Sign up to unlock" label (paywall)
- Single CTA: "Sign up to read the full analysis →"
While loading OR when the API returns `isStatic: true`, renders
the original Jokic mockup byte-for-byte. No flash-of-blank-card.
`Hero.tsx` — old `FloatingDemoCard`, `Stat`, and `row` constant
deleted. `GradePill` import moved into `LiveHeroProp`.
### Phase 3 — Soccer weather
`soccerFeatureExtractor.js` now calls `weatherService.getWeather()`
for outdoor WC venues after resolving the venue. Dome venues skip
the fetch. Unknown venues skip silently. New feature fields:
`weather_temp_f`, `weather_wind_mph`, `weather_wind_dir`,
`weather_precip_mm`. All null when skipped/failed.
### Phase 4/5 — OG tags + CSP (mostly already done)
OG meta + Twitter card + `og-image.png` were all wired in Session 9.
Existing CSP in `next.config.ts` was comprehensive. Session 16 added:
- `https://browser.sentry-cdn.com` to `script-src` (Sentry SDK)
- `https://*.sentry.io` and `https://*.ingest.sentry.io` to
`connect-src` (event ingestion). Without these the browser
Sentry client silently dropped events.
### Tests added (Session 16)
| Suite | Tests |
|----------------------------------------|-------|
| `tests/unit/sportMarkets.test.js` | 16 |
| `tests/unit/soccerWeather.test.js` | 7 |
| **Session 16 total** | **23**|
### Quality gates
- `npm test`: **1429 / 1429 passing** (1405 + 24), 110 suites, 0 regressions
- `web/npm run build`: clean
- License audit: third-party deps remain permissive
### Honest gaps
- `LiveHeroProp`'s glitch effect (scan lines + blur + fade) renders
only in a browser. Build verified. Deploy smoke-test recommended.
- Hero endpoint depends on `/api/odds/{sport}` returning populated
`props`. If upstream odds-api is rate-limited or proxies aren't
reaching Express, the static fallback fires — cold visitors see
the Jokic mockup, not live data.
- Sentry CSP entries added but require redeploy to take effect.
Until then, the browser SDK silently drops events.
### Coolify follow-ups
1. **Drop the patch.js workaround**: remove
`NODE_OPTIONS=--require /app/data/patch.js` from the web
service env. Code-layer fix in Session 16 makes the runtime
patch obsolete.
---
## Session 15 (2026-06-11) — SHIPPED