Session 15: Intelligence hardening — park factors, weather, Tank01 prefetch, pace factors, signal audit, founder pricing fix (1405 tests)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-06-11 16:21:18 -04:00
parent f5d79cf70d
commit 167996d99a
20 changed files with 1550 additions and 28 deletions
+127 -2
View File
@@ -1,10 +1,135 @@
# VYNDR — Build State
## Last Updated
2026-06-10
2026-06-11
## Current Phase
SHIP BUILD v14.0 — Africa checkout + Tank01 wiring + WNBA/MLB odds + UX polish (Session 14)
SHIP BUILD v15.0 — Intelligence hardening + platform correctness (Session 15)
## Session 15 (2026-06-11) — SHIPPED
### Phase 0 — Correctness
- **Africa short-circuit removed** (`Pricing.tsx:152`). The Session 14
backend handles 'africa' end-to-end: validation accepts it; missing
`STRIPE_PRICE_AFRICA` returns a 503 with `code:'tier_unconfigured'`
the existing inline error surface displays. The frontend short-
circuit was blocking checkout even after the backend was ready.
- **Odds + Sentry + welcome email audits**: all already correct from
prior sessions. Documented for posterity; no fixes required.
- **Poller → odds pipeline**: confirmed there's NO key-mismatch
pipeline issue. Pollers handle game resolution
(`game:{id}:status`, `poller:{SPORT}:heartbeat`); `oddsService`
populates `odds:{sport}:{date}` on-demand. The "1 games shown but
Slate empty" report would be a separate odds-api quota / key issue.
- **Founder price fallback hardened**. `PRICE_MAP` no longer falls
back to fake strings like `'price_analyst_monthly'` that would 400
from Stripe in live mode. Missing env → `PRICE_UNCONFIGURED`
sentinel → 503 with `code:'tier_unconfigured'`. Founder codes
presented against an unwired founder-price env now fall back
GRACEFULLY to the standard tier price rather than dropping the
checkout — the founder discount is operator-controlled and
shouldn't break the user's purchase.
### Phase 1 — Signal audit
Documented in a new comment block at the top of
`src/services/intelligence/computeFeatures.js`. Every signal cited
to its data source: injury (ESPN injury feed), coach (Supabase
`coach_profiles` + JSON seed), consistency (game logs via
`gameLogService`), Tank01 fields (Session 14 + 15 prefetch),
soccer cascade (Session 9), park factors (Session 15 — static),
weather (Session 15 — Open-Meteo), pace factors (Session 15 —
static). No phantom signals.
### Phase 2 — MLB park factors
`src/data/parkFactors.js` — all 30 MLB parks indexed to 100 league
average. FanGraphs 2024-25 three-year weighted data. Coors at hr=128,
SF Oracle at hr=85 (the two extremes by design). `getParkFactor()`
returns null on unknown teams so the feature extractor drops the
signal cleanly rather than falsely reporting "neutral".
Wired into `computeFeatures.js` MLB branch — features pick up
`park_hr`, `park_h`, `park_r`, `park_home` when the home team
resolves.
### Phase 3 — Weather (Open-Meteo)
`src/services/weatherService.js` — Open-Meteo proxy (no API key
required). 5-second hard timeout, 1-hour Redis cache, silent
degrade on failure (never blocks the grade). Fahrenheit + mph units
to match the bettors' mental model.
`src/data/venueCoordinates.js` — lat/lon + dome flag for all 30
MLB venues and all 16 World Cup 2026 venues. Retractable stadiums
are marked `dome:true` because operators close the roof when
conditions warrant — weather doesn't drive grade in that case.
Wired into `computeFeatures.js` MLB branch — fetches weather when
the home venue is outdoor + has finite coordinates.
### Phase 4 — Tank01 daily prefetch
`scripts/tank01-prefetch.js` — orchestrator that pulls the Redis
cache keys Session 14's `tank01Augment.js` reads. Default budget
≤80 requests/run, configurable via `--max=N`. NBA path pulls
schedule + final-game box scores + daily odds. MLB path pulls
scoreboard + final-game box scores (BvP pull awaits batter/pitcher
ID resolution on the scoreboard payload).
Recommended trigger: extend the n8n "Morning Ops" workflow to
exec the script daily at 7am UTC.
### Phase 5 — MLB matchup context
`src/services/intelligence/mlbContext.js` — pure functions for
platoonAdvantage(pitcherHand, batterHand) and
projectedPA(lineupPosition). Tested with all hand combinations +
all lineup slots. Wiring into computeFeatures is deferred until
odds-api carries those fields (it doesn't today).
### Phase 6 — NBA pace factors
`src/data/paceFactors.js` — all 30 NBA teams (NBA.com/stats 2024-25,
indexed to 100). Legacy-abbreviation aliases (NJN→BKN, NOH→NOP,
SEA→OKC, CHO→CHA) so historical lookups resolve. Wired into the NBA
branch of `computeFeatures.js``pace_factor` (player's team) +
`opp_pace_factor` (opponent).
### Tests added (Session 15)
| Suite | Tests |
|----------------------------------------|-------|
| `tests/unit/parkFactors.test.js` | 14 |
| `tests/unit/weatherService.test.js` | 14 |
| `tests/unit/tank01Prefetch.test.js` | 14 |
| `tests/unit/mlbContext.test.js` | 21 |
| `tests/unit/paceFactors.test.js` | 12 |
| **Session 15 total** | **75** |
### Quality gates
- `npm test`: **1405 / 1405 passing** (1330 + 75 new), 108 suites,
0 regressions. One pre-existing computeFeatures test was updated:
the contract used to be "ESPN failure → empty features"; the
contract is now "ESPN failure → static context augmentation
(pace, park) still surfaces."
- `web/npm run build`: clean
- License audit: third-party deps remain permissive
### Honest gaps
- Tank01 prefetch must be triggered by n8n/cron before the augmentor
reads return data. Grades work as before until then.
- BvP pull is no-op until probable-pitcher IDs land on the Tank01
MLB scoreboard projection.
- Phase 5 helpers tested but not wired — odds-api doesn't carry
batter handedness or lineup position fields today.
- Weather for soccer venues: only MLB is wired this session.
Soccer venue weather is a 5-line follow-up in the soccer extractor.
### Coolify env (Session 15 additions)
None new from this session.
---
## Session 14 (2026-06-11) — SHIPPED