Files
vyndr/specs/vyndr-roadmap.md
T
builtbykev d296e40cb6 Session 58: Phase 1 — Truth Infrastructure (2327 tests)
ledger_entries is live (migration 019 applied to prod, RLS + NULLS NOT
DISTINCT dedupe verified against the real database). Every grade now
persists, settles against the real result, and carries closing-line value.

- ledgerService: pipeline pre-grade upserts (public model record, user_id
  null, idempotent), closing capture on every snapshot (last write before
  game start = the close), settlement with SIGNED CLV (over = locked -
  closing; beat/faded/flat), 30d model aggregate with the hard n>=20 rule.
- Write paths: snapshotService -> ledger (priority path); Next /api/scan ->
  ledger for authenticated users only (anon never touches the public
  record). Refused reads write nothing and don't burn a scan.
- Honest refusal (work-order 1.5): no projection => insufficient_data,
  grade null, "INSUFFICIENT DATA - no read" UI. The web gradeAdapter no
  longer displays the line as the model projection (the audit's
  model==line / +0% edge degenerate); the card renders absent states.
  projectionFor is sport-aware (l5 -> l20 -> {stat}_per_90 -> xG).
- /ledger: MY READS | MODEL tabs; model header shows hit% + beat-close%
  only at n>=20, else RECORD BUILDING + live pending count. ModelRecord
  deferred-render strip on landing + player hero. CLV + outcome chips,
  revised_from_grade strikethrough (Phase 2.5 ready).
- SYNC (Task 5): thresholds vs SNAPSHOT_EXPECTED_INTERVAL (normal <1.5x,
  amber >=1.5x, STALE red >=3x) via /api/snapshot/summary.
- Phase 2.5 logged in specs/vyndr-roadmap.md (build after Phase 3).
- Data-semantics hardening: strict null-safe numeric parsing everywhere a
  market value is handled (Number(null)===0 would have fabricated lines).

Backend 2309 -> 2327 tests (201 suites), web build exit 0.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 21:34:26 -04:00

126 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VYNDR Master Roadmap — Generated 2026-07-10 (Session 56)
The foundation document. Derived from the Session-56 data audit
(`specs/propline-audit.md`, verified against live MLB Stats / ESPN / Odds APIs).
Every session after this references it. Update it as sessions ship.
---
## Current coverage (verified 2026-07-10)
| Sport | Odds props | Grading | Features (l5/l20) | Outcomes settled | Status |
|-------|-----------|---------|-------------------|------------------|--------|
| MLB | ✅ PropLine + odds-api | ✅ | ✅ real (statsapi) | ✅ (S55, expanded S56) | **LIVE** |
| WNBA | ✅ odds-api/PropLine | ✅ | ⚠️ Python offline → thin | ❌ | **PARTIAL** |
| Soccer | ✅ odds-api only (no PropLine) | ⚠️ extractor only | ⚠️ | ❌ | **PARTIAL** |
| NBA | ⚠️ summer league only | ✅ | ⚠️ offline | ❌ | **OFF-SEASON (Oct)** |
| NFL | ⚠️ preseason | ⚠️ mapped, not e2e | ❌ | ❌ | **PRE-SEASON (Sep)** |
| NHL | ❌ off-season | ⚠️ mapped | ❌ | ❌ | **DARK (Oct)** |
**The core product is MLB.** It is the only sport that is live end-to-end
(odds → grade → real features → settled accuracy). Everything else is a
build-out target.
---
## Gap analysis (from the audit)
1. **Outcome settlement is MLB-only** — WNBA/NBA/soccer grades never settle, so
the accuracy record (the S55 trust engine) only reflects MLB. **Highest-value
gap.** Needs the ESPN box-score settle path (`espnStatsAdapter` already parses
the shape for season avgs).
2. **NBA/WNBA features depend on an offline Python nba_api service** — l5/l20 are
often empty; only season avgs survive (ESPN fallback). Needs an ESPN game-log
feature path so intel populates without the Python service.
3. **Soccer has no PropLine source** — odds-api backup only; no `SPORT_KEYS` entry.
Soccer grading isn't wired into the snapshot pipeline end-to-end.
4. **NFL mapped but not graded end-to-end** — markets normalize, but no feature/
outcome path. Wire before September.
5. ~~RBI silent-failure (rbis/rbi)~~**FIXED S56.**
6. ~~Under-requesting MLB markets~~**FIXED S56** (6 → 12 markets).
7. ~~No pipeline alerting / retry / missed-cron~~**FIXED S56.**
8. **No live calibration** — the accuracy record exists but doesn't yet feed back
into grade confidence (spec 2.3 from S55).
9. **`batter_strikeouts` (batter Ks)** mapped but not feature/settle-wired.
---
## Session plan (priority-ordered)
| Session | Focus | Ships | Scope |
|---------|-------|-------|-------|
| 57 | **WNBA/NBA outcome settlement** | ESPN box-score settle path in outcomeService → accuracy for basketball; WNBA goes fully live | M |
| 58 | **ESPN game-log features** | l5/l10/l20 for NBA/WNBA without the Python service → real intel on basketball grade cards | M |
| 59 | **Soccer end-to-end** | soccer into the snapshot pipeline (odds-api source), feature extractor wired, ESPN settle | L |
| 60 | **Live calibration** | accuracy record feeds grade-confidence adjustment (under/over-confident tiers nudge); "Model health: Calibrated/Learning" indicator | M |
| 61 | **NFL pre-season prep** | NFL feature + settle path (ESPN NFL boxscore) so Week 1 (Sep) is live | L |
| 62 | **Prop breadth** | `batter_strikeouts`, `triples`, pitcher `walks`; NBA `pra`/`turnovers` requested; WNBA extra markets | S |
| 63 | **Accuracy depth** | per-archetype hit rates, per-stat hit rates, player-level record ("VYNDR on Judge: 12-5"); ledger UI | M |
| 64 | **NBA regular-season readiness** | verify NBA pipeline for the Oct tip-off; depth-chart/cascade live | M |
Scope key: S ≈ ½ session, M ≈ 1 session, L ≈ 12 sessions.
> NOTE (Session 58): the numbered plan above predates the overhaul work
> order. The work order's phases (0 = kill the lies ✅ S57, 1 = truth
> infrastructure ✅ S58, 2 = slate UX, 3 = mobile, **2.5 below**, 4 = scan/
> parlay, 5 = records, 6 = landing/content) take sequencing priority; the
> table's items slot in where they don't conflict.
## Phase 2.5 — Intraday line refresh + directional movement (LOGGED Session 58)
Authoritative scope from the Phase 1 GO prompt. **Build after Phase 3
(mobile), before Phase 4.** During slate hours (~noonmidnight ET):
- Lightweight ODDS-ONLY refresh every 1530 min (no full re-grade run).
Recompute the signed delta per graded prop RELATIVE TO THE GRADED SIDE —
direction is the signal, not magnitude alone.
- **Moved WITH the grade** (market chasing our number): PropRow badge
`STEAM ▲ +N`. Good for the record; entry edge compressed. No re-grade.
- **Moved AGAINST the grade ≥ 1.0** (or odds-equivalent): auto re-grade THAT
PROP ONLY.
- Grade holds → badge `VALUE ▲ better entry` (better number, same read).
- Grade drops → update the grade with `revised_from_grade` set (column
already exists in `ledger_entries`) + a visible revision marker
(original grade struck through — the ledger UI already renders it).
Revisions are ALWAYS public — never silently regrade, per Ledger ethos.
- All displayed lines remain real book values from the refresh — the
refresh CAPTURES market numbers, never computes them.
- After ship: drop `SNAPSHOT_EXPECTED_INTERVAL` to the refresh cadence —
the HeartbeatBar SYNC badge (normal <1.5x · amber ≥1.5x · STALE ≥3x)
goes genuinely live with zero further UI changes.
---
## Stat-type coverage target (fully built)
- **MLB (batters):** hits, total_bases, home_runs, rbi, runs, doubles, triples,
walks, stolen_bases, batter_strikeouts. **(pitchers):** strikeouts, earned_runs,
hits_allowed, innings_pitched, outs, walks. *(Bold-new this session: doubles,
triples, outs, runs, walks requested + settleable.)*
- **NBA/WNBA:** points, rebounds, assists, threes, steals, blocks, turnovers, pra.
- **Soccer:** goals, shots, shots_on_target, tackles, cards, corners, saves,
passes, clean_sheet.
- **NFL:** passing/rushing/receiving yards, receptions, pass/rush/rec TDs,
anytime_td, interceptions, kicking_points.
## Sport coverage target
- **MLB** ✅ live now (settled accuracy).
- **WNBA** → live after S57 (settlement) + S58 (features).
- **Soccer** → live after S59.
- **NFL** → ready for September (S61).
- **NBA** → ready for October (S64).
- **NHL** → ready for October (fast-follow once NBA path exists; shares ESPN pattern).
---
## Operating invariants (do not regress)
- Three stat_type whitelists stay in sync: `routes/analyze.js`, `routes/scan.js`,
`python/utils/validation.py`.
- A requested market MUST have a `MARKET_MAP` entry (else silent zero) AND, if
MLB, a `MLB_LOG_FIELD` entry in BOTH `featureCache` and `outcomeService` (else
no features / no settlement).
- The accuracy pill stays HONEST — "LEARNING" below MIN_SAMPLE, never a faked %.
- Pipeline alerts (ntfy) fire on success/failure/stale/overdue; the status probe
(`GET /api/internal/snapshot/status`) exposes `overdue`.