Session 7j: Soccer intelligence - 9 leagues, 11 signals, 6 traps, poller, prefetch, 131 new tests (1173 total)

This commit is contained in:
Kev
2026-06-10 14:50:13 -04:00
parent b9084408bf
commit ad5ea8d5a8
28 changed files with 3175 additions and 49 deletions
+31
View File
@@ -60,6 +60,7 @@ Mounted in `src/app.js`. Auth column meanings:
| GET | /api/health | public | n/a | `app.js` (inline) |
| GET | /api/odds/nba | public | 10mb | `routes/odds.js` |
| GET | /api/odds/ncaab | public | 10mb | `routes/odds.js` |
| GET | /api/odds/soccer/:league | public | 10mb | `routes/odds.js` (Session 7j) |
| POST | /api/analyze/prop | public + 10/min IP | 10mb | `routes/analyze.js` (cached 60s) |
| POST | /api/analyze/batch | public + 10/min IP | 10mb | `routes/analyze.js` (cached 60s) |
| POST | /api/scan/parlay | user | 10mb | `routes/scan.js` |
@@ -190,6 +191,14 @@ back). Updated this session in Section 1 of Session 7c.
| `PINNACLE_API_BASE` | ✓ commented (legacy) |
| `ODDS_API_KEY` | ✓ commented (legacy) |
### Soccer / World Cup 2026 (Session 7j)
| Var | Required | Default | Used By | Doc? |
| ------------------------- | -------- | ------------------------------------------------ | ------------------------------------------------------- | ---- |
| `FOOTBALL_DATA_API_KEY` | no | (none) | `footballDataAdapter`, `soccer-data-prefetch` | ✓ |
| `SOCCER_LEAGUES` | no | `WC` | `poller/soccer.js`, `soccer-data-prefetch` | ✓ |
| `WORLDCUP_API_URL` | no | `https://worldcup2026-api.up.railway.app/api/...` | `poller/soccer.js` | ✓ |
| `RAPID_API_KEY` | no | (none) | reserved for `soccer-data-prefetch` referee enrichment | ✓ |
### Engine 2
| Var | Doc? |
| ---------------------------- | ---- |
@@ -235,6 +244,11 @@ back). Updated this session in Section 1 of Session 7c.
| `VYNDR_API_URL` | `http://localhost:3001` | ✓ commented |
| `OFF_HOURS_POLL_MS` | hardcoded 5min | not env |
PM2 ecosystem (Session 7j) — four poller processes per container:
- `poller-nba`, `poller-wnba`, `poller-mlb` (box-score resolution path via `poller/poller.js`)
- `poller-soccer` (fixture indexing via `poller/soccer.js` — different
data sources and cache shape; honors `SOCCER_LEAGUES` env)
### Backup + Ops
| Var | Doc? |
| ------------------------- | ---- |
@@ -347,6 +361,10 @@ Source: `grep -rn "cacheSet\|cacheGet\|redis\.set"`.
| Resend (email) | `web/src/services/email.ts` | `RESEND_API_KEY`, `RESEND_FROM_EMAIL` | n/a | transactional email |
| NexaPay | `web/src/services/nexapay.ts` | `NEXAPAY_*` | n/a | checkout fallback |
| PostHog | `web/src/lib/analytics.ts` | `NEXT_PUBLIC_POSTHOG_KEY/HOST` | n/a | browser analytics |
| football-data.org | `footballDataAdapter.js` | `FOOTBALL_DATA_API_KEY` | 10/min (8 enforced) | poller-soccer, prefetch |
| Stripe | `services/stripeService.js` | `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_*` | n/a | checkout + webhook |
| The Odds API | `services/oddsService.js` | `ODDS_API_KEY` | quota tracked | per-sport odds endpoints |
| worldcup2026 OSS | `poller/soccer.js` | `WORLDCUP_API_URL` | none (free) | WC fixture poll |
---
@@ -484,6 +502,19 @@ No circular imports detected.
Full removal blocks on ARCH-1 Step 6 — the legacy book adapters
retire together with the legacy grading path.
- **[ARCH-3] Soccer pipeline added as a parallel branch.** Severity:
Info. Status: **SHIPPED in Session 7j.** Soccer routes off
`computeFeaturesForProp` to `soccerFeatureExtractor` when
`sport ∈ {'soccer','football'}`; trap detection branches on the same
in `getTrapScore`; reasoning branches in `buildConcreteReasoning`.
Engine1 is sport-agnostic (passes unknown feature keys through).
Data flow: `poller/soccer.js` writes per-team `nextmatch` /
`lastfixture` pointers; `scripts/soccer-data-prefetch.js` writes
per-player + per-team-defense aggregates. The feature extractor
reads ONLY from cache — no external HTTP on the user request path.
Day-1 gap: xG fields (`xg_per_90`, `xg_delta`) are null until the
soccerdata-Python bridge ships; engine handles the nulls gracefully.
### SEC — Security
- **[SEC-1] `/api/analyze/batch` has no auth or rate limit.** Severity: