# VYNDR — STATE OF THE WORLD ### As of `45aa8ca` (main, deployed live), 2026-07-14. This file opens every future session. Update it when a train ships. ## SHIPPED — TRUTH-EVERYWHERE + OFFSEASON HUB TRAIN (on main, deployed) Governed by `specs/truth-everywhere-train.md`. **The reframe that drove it: only MLB GRADED in prod (NBA/WNBA refused their whole slate — offline Python feature source, no fallback; soccer errored pre-grade). So grading, not settlement, was the first domino.** Standing deploy authority granted (green + fingerprint → merge + deploy, no ask). Waves DEPLOYED: - **Wave 0 — NBA/WNBA GRADE (free ESPN gamelogs):** `espnStatsAdapter.getPlayerGameLog` feeds `featureCache` when the Python source is null → l5/l20 → props grade. **Verified live** (A'ja Wilson→B, Ionescu→C off real ESPN form). Endpoint `site.web.api.espn.com/apis/common/v3/sports/basketball/{nba|wnba}/athletes/{id}/gamelog`. - **Resolver-hardening:** roster-index primary resolver (206 WNBA / 544 NBA) + `?season=` gamelog retry — the real fix for dual-league (WNBA+NCAA) players who got an empty gamelog (Collier/Brionna Jones now resolve). Broad coverage. - **Wave 1 — NBA/WNBA SETTLE:** `defaultGetPlayerStats` routes nba/wnba to the ESPN gamelog in BOTH `outcomeService` + `ledgerService`; separate `NBA_BOX_KEY`/`NBA_COMBO` settle maps; final-honesty guard (never settle in-progress); `opsWatch` finals-gated zero-settle alarm + per-sport boot announce. accuracy:{sport}+by_tier light up automatically. **PROOF is next-day** (grades lock tonight → settle tomorrow → `accuracy:wnba`). - **Wave 2 — NEVER-DARK HUB:** ESPN `/news` wire (`newsService` + `/api/news/:sport`) — **verified live (real headlines)**; `NewsWire`+`FuturesBoard` on `/explore` (offseason-aware, self-hide on empty, futures "TRACKED · NOT GRADED"). **KNOWN GAP: `/api/futures/:sport` returns `markets:[]` for ALL sports in prod** — code path looks correct (odds-api `/sports/{FUTURES_KEYS}/odds?markets=outrights`); needs a live odds-api probe (ODDS_API_KEY) to finish. Self-hides honestly meanwhile. `FUTURES_ENABLED=0` kill-switch; 12h TTL; 1 credit/refresh. - **Also rode along:** `espnAthleteIndex` cross-sport headshot capture (headshot-coverage) — NBA/WNBA/NFL/NHL real headshots when in-season. DEFERRED (locked decisions): **soccer** — grade+settle path, needs `API_FOOTBALL_KEY` (VALIDATED, Free 100/day, active to 2027 — set it in Coolify env `API_FOOTBALL_KEY`; code dormant until wired) + ESPN-soccer-free-first attempt; **MMA moneyline settle** (combat not yet in the grade/lock loop); **NFL/NHL** (dormant, in-season later); **futures debug** (odds-api probe). Test baseline now **263 suites / 3183**. ## SHIPPED — WIRING & DATA TRAIN (on main `3b1aa9f`, deployed 2026-07-13) Governed by `specs/wiring-data-train.md` + `specs/combat-intelligence.md` + the global visual reference `specs/design-reference/vyndr-system.html` (build toward it; live wordmark kept). **All 6 waves shipped + deployed** (fingerprint-verified: /parlay, /fight, /u/vyndr live; house record 88-38/70% real). Real assets verified live (MLB/ESPN headshot CDNs, ESPN-MMA feed, Baseball Savant CSV). - **Wave 1 — trust bugs:** billing renewal honest render (`billingDisplay.classifyRenewal` — no far-future placeholder); James Wood nameKey-collision fixed (`mlbStatsAdapter` teamHint disambiguation + streaks join-invariant); DeskShowcase "$1M terminal" → deadpan copy. - **Wave 2 — sport-agnostic entity layer:** real player headshots threaded from ingestion (MLB MLBAM + NBA/WNBA ESPN athlete ids that were fetched-and-discarded) across slate/scan/hotlist/search/grade card; soccer = honest monogram (no free id); 8 self-authored SVG book wordmarks (`web/public/books/*.svg`, swappable for official art) + all 10 book keys resolve; team-logo abbr aliases. Storage: id on the `enriched` grade at `snapshotService` (zero new I/O). - **Wave 3 — record by grade tier (Addition 2):** ONE shared `TierRecord` (`lib/tierRecord.js` + component) on dashboard + /u + ledger; per-tier W-L always, hit-% only at n≥20 per tier (gate stays in `getModelAggregate`). - **Wave 4 — missing surfaces:** Outlook mode (grid never blank → yesterday receipts / tomorrow schedule); Market-Breadth median-consensus-vs-model strip (self-hides <2 books); Parlay Lab `/parlay` (slate-independent leg source); live Grade-Shift timeline (`GradeShift`). - **Wave 5 — /u house-mode + arsenal:** house handle `vyndr` surfaces the real `user_id=NULL` public model record + per-tier calibration + 1080×1350 portrait/OG (user-handle privacy 404s stay byte-identical); Baseball Savant pitcher-arsenal (`savantAdapter`, free CSV, verified) → `PitcherArsenal` card, self-hides on absent. - **Wave 6 — combat v1 (MMA):** ESPN-MMA fight cards + tale-of-the-tape + style-blend archetypes (sport-scoped `COMBAT_ARCHETYPES` — FINISHER color collided w/ soccer + tripped the ΔE gate, so kept separate) + odds-api ML/round-totals + style-edge verdict. NOT in the snapshot/settle loop; method/round/props + fighter photos + matchup-GRADE engine + ufcstats scraping all DEFERRED, flagged data-limited in-UI. New routes: `/fight/[id]`, `/parlay`, `/u/[handle]/portrait`. POST-MERGE TODO: NBA/WNBA headshot coverage + combat depth need prod runtime verification; soccer headshots blocked on `API_FOOTBALL_KEY`; combat settlement + matchup-grade engine are the next combat sub-wave. ## SHIPPED — DESIGN TRAIN v2 (merged to main `e9c0a59`, deployed & fingerprint-verified live) Governed by `specs/DESIGN-SPEC.md` v2 (the raised standard: entities render as themselves, color contract, sub-200ms, screenshot-first billboards). **All six sessions DS0–DS5 shipped: merged to main, pushed to gitea, Coolify auto-deploy LANDED — verified live via `/pricing` DeskShowcase ("A $1M terminal") + `/u` route serving the unified EmptyState. 238 suites / 2885 tests green, `next build` exit 0.** Post-ship TODO: re-run the Chrome design audit against prod v2 to confirm all 22 audit findings closed (env here can't screenshot — WSL2↔Windows-Chrome localhost unreachable). - **DS0 Entity Layer** (`24af247`): `teamMeta.js` (real logos+colors for 30 MLB / 30 NBA / 13 WNBA / 48 WC nations via ESPN CDNs), `TeamLogo` / `PlayerAvatar` (team-colored monogram fallback, no gray silhouette) / `BookWordmark`, swapped into GameCard/StatStrip/StreaksPanel/TeamHub. - **DS1 Speed + Trust Bugs** (`1c681df`): React #418 hydration fix (mounted-flag guard), layout-matched `Skeleton`s replace text-wall loaders, scan→ledger persistence via `session.access_token`. **DS1 follow-up** (`cf91c04`): closed the sb-token trust-bug CLASS — `lib/authToken.js` `currentAccessToken()` reads the REAL Supabase session (not the OAuth-only `sb-token` key) across profile/slip/dashboard/settings/tracker. - **DS3 Color Contract** (`49a3323`): `lib/colorContract.js` — green = one meaning (edge/A-tier/CTA), edge/CLV by sign, glow A/A+ only, archetype greens deduped. Enforced by `colorContract.test.js` (fails on violation). - **DS4 Billboards** (`45bafbc`): STREAKS row (length as mono hero), grade reveal (sign-colored edge), CLV reframe (flat says so), `/u` public profile + OG. - **DS2 Dashboard Slate Rebuild** (`fe294a5`): never-empty hero (falls back to yesterday's A-tier settled receipts), one bold hero per card, pending-filler collapse, ranked tonight-grades. `slateAdapter.js` engine + 21 tests. - **DS5 Pricing + Motion + States** (`a18a3f3`): Desk $44.99 as the hero tier (single green CTA, `DeskShowcase`), ticker → punctuated stillness (≥4.2s hold, one idle motion), unified `EmptyState` (404 grammar) at TeamHub/game/ledger, archetype glyphs propagated to streaks/ledger. NOTE: WSL2↔Windows-Chrome localhost is unreachable in this env, so visual proof was SSR-render + source-assertion tests + live prod fingerprint curls, not pixel screenshots. The `design/v2-train` + per-DS worktree branches can be pruned. Stale `origin` GitHub remote still carries the long-dead leaked PAT — deploy runs off `gitea`, so it's inert, but rotate it when convenient. ## WHERE THINGS STAND **Tests: 2757 / 229 suites, all green. Web build exit 0.** Baseline at the A1 board's start was 2398. Governing docs: `specs/VYNDR-NORTH-STAR.md` (v1.1) + `specs/VOICE.md` (v1.1) + `specs/ROW-GRAMMAR.md` — read them before building anything. **The record is live and writing itself.** Day one (Jul 11): 24→25 MLB rows, real locks (line/odds/book), closing capture every snapshot + every 20-min intraday refresh, settlement each morning slot with signed CLV. No percentage renders anywhere under n≥20 — enforced in `ledgerService.getModelAggregate`, the single gate. WNBA/soccer rows pend honestly until Phase 4.5. ## THE THREE A1 TRAINS (all merged to main, all pushed) | Train | Range | Carried | |---|---|---| | #1 | `4d2b27d → 219167e` | S0 verify · S1 promise audit (`PROMISE-AUDIT.md`: alt ladder + quarter-Kelly BUILT, analyst unlimited, "40+" claims made honest) · S2 compliance (/responsible-gambling rebuilt, /terms /privacy drafts, /methodology, 5 seed articles in `content/articles/`) · S3 affiliate (BOOK IT deep links organic-until-config-flip, best-price dots, ?ref= partner attribution) · S4 media engine (`mediaEngine` + executable VOICE lint, `/desk` arsenal + DATA BRIEF, Ghost DRAFTS-only publisher) · S5 viability (statsapi lineups → CONFIRMED/NOT-IN kills grades visibly; ESPN injury wire; Yesterday/Today/Tomorrow nav + Settle panel) · S7 newsletter (Listmonk capture + THE VYNDR REPORT assembly, env-gated) · S8 ops (settle alarm, 3-slot failure pager, quota/disk/mem, 9AM pulse — test alert delivered to the real ntfy channel) · S9 slip reader (tesseract.js OCR + per-book parsers, /slip page; synthetic-image acceptance passed) · S10 public profiles (/u/{handle}, private-by-default, no-existence-leak) · migrations 021/022 | | #2 | `219167e → 14dc9cf` | S6 display (ROW-GRAMMAR.md + slot-order fixes, line sparklines from intraday history, last-10 ●●○ dots, CLV distribution on MODEL tab, ⌘K SearchModal + mobile search, landing LCP fixes) | | #3 | `14dc9cf → ec5ee3d` | S11 live tracking (statsapi/ESPN live box lines → ON PACE / NEEDS N / HIT ✓ in the outcome slot, live games float up, "TRACKING — READ LOCKED PRE-GAME"; real-live acceptance: Harper 3/1.5 TB ▼8th → HIT ✓). Grades never change in-game. | **Deploy status:** ALL LIVE as of `17fb981` (Jul 12 17:43 UTC). Fingerprints confirmed in prod: `/api/live/mlb` serves JSON with `hasLive:true` (S11 live tracking working — a real game was tracked at confirm time), `/api/health` healthy, `/api/snapshot/summary` → `expected_interval_s:1200` (env var now set). **P0 postmortem (`17fb981`):** the API silently served a 14h-old image (`4d2b27d`) from Jul 11 ~20:00 to Jul 12 17:43. Root cause: the Dockerfile never copied `content/`, and `mediaEngine.js` read `content/stark-lines.json` with an unguarded module-load `readFileSync` → ENOENT at require time crashed `app.js`'s eager route chain (app → routes/desk → deskService → mediaEngine) → boot failed → Coolify healthcheck rolled back. Every merge from S6 onward was merged-but-not-running for 14h. Fixed: Dockerfile COPYs content/; mediaEngine treats the file as optional garnish (loadStark try/catch → {}); `src/preflight.js` now prints `[preflight] OK|DEGRADED` at boot so a missing file/env is legible instead of masked by a rollback. LESSON: any module-load file read is a boot-crash risk — keep them lazy + try/catch, and the Dockerfile must copy every dir the runtime reads. ## LIVE INFRA MAP (verified in prod) - **VYNDR Web** — Next.js :3000, vyndr.app. **VYNDR API** — Express :3001, api.vyndr.app (`/api/internal/*` behind `VYNDR_INTERNAL_KEY`). - **Scheduler (in-process, API app, `SNAPSHOT_CRON=1`)** — snapshot slots 14,19,22,1,3 UTC; settle pass (outcomes + ledger, idempotent) runs FIRST each slot; 20-min intraday odds refresh during slate hours (`INTRADAY_REFRESH=0` kills); desk-ready ping after the day's first slot; daily pulse 13:00 UTC; missed-cron + 3-slot-failure + quota pagers → ntfy `vyndr-pipeline-kev2026`. Boot logs: `[snapshotScheduler] armed` + `[settlement] armed`. - **Data**: Redis (mastermind-cache) for all pipeline caches (SNAP_TTL 24h — do NOT shorten; the 11h overnight gap killed morning settles once). **Supabase = the ledger** (`ledger_entries` + `public_profiles` + `user_profiles.partner_ref`; RLS everywhere, service-role-only writes; migrations 001–022 applied, files in `supabase/migrations/`). - **Deploys**: gitea `git.builtbykev.com/builtbykev/vyndr` → Coolify webhook (HMAC secret fixed Jul 11; auto-deploy proven). GitHub `origin` remote is dead-PAT hygiene debt only. - **Free feeds** (zero out-of-pocket law): PropLine ×3 keys (9k/day capacity; intraday uses ≤144/day), odds-api backup (quota-gated), statsapi.mlb.com, ESPN site API. tesseract.js OCR self-hosted (traineddata caches on first call — needs outbound network once). ## ENV VARS STILL PENDING ON KEV'S SIDE (Coolify, API app) 1. `SNAPSHOT_EXPECTED_INTERVAL=1200` — **confirmed absent** (fresh restart still served the 18000 default). SYNC badge tracks intraday cadence once set. 2. `DESK_OWNERS=kevdevelops@gmail.com` — /desk 403s everyone until set. 3. `LISTMONK_URL` / `LISTMONK_USER` / `LISTMONK_TOKEN` / `LISTMONK_LIST_ID` (all four together) — newsletter no-ops politely without them; box-side setup in `docs/NEWSLETTER.md`. 4. Optional: `GHOST_URL` + `GHOST_ADMIN_API_KEY` (drafts), `PULSE_HOUR_UTC`. **Entity placeholders** (S2, blocked on Kev's values): `[ENTITY NAME]`, `[STATE OF FORMATION]`, `[ARBITRATION VENUE]`, `[CONTACT EMAIL]` in `web/src/app/terms/page.tsx` (6×) + `web/src/app/privacy/page.tsx` (5×). A test keeps unverified entity names out until replaced. ## HONEST OPEN ITEMS - **Phase 4.5 — WNBA settlement via ESPN box scores. DUE ~Jul 24** (hard date). Until then WNBA/soccer/NBA ledger rows pend forever; accuracy = MLB only. - **The record needs time**: no percentage anywhere before n≥20 settles, by design. First percentages expected ~day 3–4 of MLB volume. - **Slip reader**: acceptance was a clean synthetic image; needs one real phone screenshot (DK) to validate OCR robustness. - **Landing LCP**: S6's fixes are static-analysis-provable but unmeasured — run PageSpeed mobile vs the 3.8s baseline; next suspect is the LiveHeroProp post-fetch swap. - **Partner report endpoint** returns real numbers only for signups AFTER migration 021 (metadata path live; OAuth signups carry no ref — known gap in `docs/PARTNERS.md`). - **NBA Oct / NFL Sep readiness** + soccer end-to-end: `specs/vyndr-roadmap.md`. - **Killed permanently** (founder ruling): AI chat assistant, light mode. ## OPERATING LAWS (never relax) Zero out-of-pocket · absent beats wrong, refusal beats hollow · n≥20 before any percentage · nothing auto-posts anywhere, ever · grades never change in-game (revisions are public via `revised_from_grade`) · one meaning per color, data in mono · no exclamation points, anywhere · the pipeline is the only source of numbers, including in marketing (`mediaEngine` lint enforces). — STATE as of ec5ee3d · the record is writing —