b354d1d088
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
224 lines
26 KiB
Markdown
224 lines
26 KiB
Markdown
# VYNDR — STATE OF THE WORLD
|
||
### As of `a8e383e` (main, deployed + fingerprint-verified live), 2026-07-18. This file opens every future session. Update it when a train ships.
|
||
|
||
## TRUTH-EVERYWHERE PART 2 — COMPLETE (2026-07-18, on main, all 8 items fingerprinted)
|
||
Standing law: NO FABRICATED DATA ANYWHERE (UI, marketing, demos, examples). If it renders a number, it's from the DB or it doesn't render. Live data IS the better demo.
|
||
- **Item 1 — Verb law** (`66d52a9`): one verb, READ (never SCAN), swept every user-visible surface (FAB/nav/tier/ticker/upgrade). `tests/unit/verbLaw.test.js` enforces. Ticker "slate read" text flips at the next snapshot cron.
|
||
- **Item 2 — Founder counter** (`41fc2b9`): real active-paying-founder count from Stripe-synced user_profiles, cached; HIDES when unavailable. Live: **1/100** (was fabricated 47/100).
|
||
- **Item 3/4 — Reports**: full fabricated-data inventory + pricing claim audit (LIVE/PARTIAL). Alt-ladder/Kelly/correlation are LIVE in the engine; scratch-cascade + real-time-feed are PARTIAL.
|
||
- **Item 5 — Daily hero prop** (`9b9aab4`): live RULE = largest |model−consensus| gap, A/B only. Shows book line vs VYNDR model + real timestamp. Empty→most-recent-real; nothing→hidden. NO static Jokic fallback. `heroPropService` + `/api/hero-prop`. Live: Erica Wheeler 9.5 vs 14.2.
|
||
- **Item 6 — Desk showcase** (`cb3237c`): real alt-line ladder from the snapshot (edge guarded on small-line artifact); φ = real same-team correlation for two real legs or hidden; Kelly tile REMOVED (no odds → can't size honestly). `deskShowcaseService` + `/api/desk-showcase`. Live: Vandersloot ladder.
|
||
- **Item 7 — Accuracy → TRUTH** (`89a2977`): public 30D accuracy (`/api/accuracy` + `/api/ledger/accuracy`) now reads the CLEAN Postgres ledger aggregate (getModelAggregate/getAccuracyView, model_value>0), not the Redis outcome log. Number moved 67%→61%. **BEAT CLOSE suppressed** at source (`clvCaptureReliable()`) — see C4.
|
||
- **Item 8 — Blog** (`3b7a1f5`,`a8e383e`): wired the 5 REAL articles (content lives in `web/content/articles` — the runtime content root, NOT repo-root content/) with honest dates + markdown render + OG. Backdated 2026-03-22 orphan deleted + 301. Live: 5 articles serve.
|
||
|
||
### 🔴 C4 — CLV CAPTURE BROKEN (open, backend)
|
||
`captureClosing` re-records the LOCKED line as "closing" (closing_line == locked_line on the whole sample) → CLV is flat → BEAT CLOSE = a measured-wrong 0%. Suppressed on all public surfaces until fixed. Full write-up + fix plan: `specs/audit-data/clv-capture-broken.md`. Flip `CLV_CAPTURE_RELIABLE=1` after C4 verifies real closes.
|
||
|
||
## PHONE AUDIT PUNCH LIST — COMPLETE (2026-07-17, on main, deployed, fingerprinted)
|
||
Kev's 35-screenshot real-390px audit (signed-out + Desk signed-in). Worked top-down, fingerprinted the batch (compare "on current form" literal live in served chunk off `71e35e9`). Test baseline: 268 suites / 3239.
|
||
- **P0-1** blue-B/amber-C banished → token-derived grade badges (earlier batch).
|
||
- **P0-2** image pipeline (logos/headshots) `loading=eager` (earlier batch).
|
||
- **P0-3** player grouping/ladders across leaders/explore/ledger/breadth (`lib/playerGrouping.js`).
|
||
- **P0-4** mobile app bar collapsed to one line (ticker+heartbeat hidden <768, `MobileSyncClock`).
|
||
- **P1-5** 390px overflow containment (breadth rows stack, pitcher line wraps).
|
||
- **P1-6** (`ff0d3b1`) ONE freshness source: Slate "UPDATED" now reads the pipeline `refreshed_at` (the app-bar clock's field), not the client poll time — no more "0s ago" vs "SYNC 46:03" contradiction.
|
||
- **P1-7** (`d755b43`) flat edge board: an impossible `|edge|>40` is treated ABSENT at both data (can't rank a fake +140% over a real +8.4%) and display layers. Chips render now (P0-2). `EDGE_BOARD_SANE_MAX`.
|
||
- **P1-8** (`00537eb`) Compare verdict truth: no phantom "edge tonight" for July NBA(0) → "on current form"; cites what actually wins (scoring/boards/playmaking, not usage).
|
||
- **P2-9** (`77e8937`) leaderboard stat labels `web/src/lib/statAbbrev.js` (SB/ER/TB) + **FLAGGED** the backend grade degradation (below).
|
||
- **P2-10** (`71e35e9`) ledger read-card density (padding/margins tightened; archetype showDesc kept — it's inline, zero height cost).
|
||
|
||
### 🔴🔴 BACKEND FINDING — MLB GRADE PIPELINE DEGRADED (needs backend work, NOT frontend)
|
||
Diagnosed against live `/api/snapshot/mlb` (2026-07-17). Full write-up + do-not list: `specs/audit-data/mlb-grade-degradation.md`. Frontend guards (sane-edge cap, statAbbrev) are damage control, NOT the fix.
|
||
- **projection == 0 for 9/25** grades — the MLB feature path feeds 0 instead of refusing (S58 `insufficient_data`). This also breaks `computeEdge` → the broken edge_pct.
|
||
- **edge_pct on a broken scale** {20,60,100,140} — not a market %. Real prop edge is single-digit.
|
||
- **grade↔confidence mismatch on 25/25** — B shown at conf 55 (= the B- band per `grade_thresholds.json`); systematic one-sub-tier gap. The surfaced `confidence` is not the probability that derived the letter (likely the data-sufficiency penalty applied to display-only). This is the audit's "B at 45%" — NOT a frontend labeling or threshold bug; DO NOT re-letter/re-threshold on the frontend.
|
||
|
||
## DESIGN COMPLETION TRAIN — SUBSTANTIALLY SHIPPED (2026-07-16, on main, deployed) · ONE MASTER CHROME AUDIT PENDING
|
||
Goal: the live product matches Design's mockup at EVERY width. **HONESTY LAW: I cannot see rendered output (WSL2↔Chrome unreachable) — every visual change is "built to Design's spec, VISUALLY UNVERIFIED." Kev runs ONE master Chrome audit at the END (390px + desktop) against the running unverified list below; fixes propagate through tokens (surfaces inherit, don't fork).** Design package (3 revs) authoritative at `specs/design-reference/` (HANDOFF.md = entry point; `vyndr-system.html` desktop, `vyndr-mobile.html` 19 screens, `vyndr-landing.html`, `assets/glyphs/` 83 SVGs+MANIFEST).
|
||
SHIPPED + deployed:
|
||
- **Palette alignment** (`e0a26a8`,`c3d469e`,`ebaab00`): tokens → Design's EXACT ramp. **Grade colors corrected: B blue→WHITE #F0F0F0, C amber→GREY #B8BCC8, D #FF4757** (the old blue/amber violated DESIGN-SPEC's own "B neutral-bright, C muted"). Surfaces #0E0E14/#14141E, text #F0F0F0/#B8BCC8/#707080, borders #1E1E2A/#2A2A38. vyndrTokens + tests aligned. ShareCard/OG billboards grade-B white.
|
||
- **Glyph system** (`77d8fd6`,`771d8b0`): 44 archetypes render Design's REAL marks (83-mark set: 74 display + 9 classifier-legacy), front `lib/archetypes.js` + back `archetypeService.js` color-synced. ZERO generic placeholders left. Combat keeps its char-glyph namespace (separate test).
|
||
- **Entity layer VERIFIED already-complete** (DS0/Wave 2A/2B — the audit was stale): TeamLogo (real ESPN CDN, monogram fallback) on game cards/team hub; PlayerAvatar headshots MLB/NBA/WNBA + NFL/NHL resolver ready + soccer-monogram (blocked) + combat-no-photos; books via bookInfo/BookChip everywhere (fixed BookComparison holdout `1558710`); line-shopping surfaces (detectBestBook/MarketBreadth) self-deepen. No `$1M` copy.
|
||
- **Rev 3 matchup chips** (`f24f941`,`baf977f`,`bf8bf16`): `TeamChip` primitive (real TeamLogo at 10-12px + abbr, inherits row opacity) on StatStrip board rows, grade reveal, pitcher-vs. Game-card headers already had TeamLogo.
|
||
- **Mobile: M1a header collapse** (`a10227e`) · **M1b app-bar clock** (`7c7ab24`, Hybrid: wall clock rests, STALE reacts off refreshed_at) · **grade-reveal mobile** (`baf977f`, 74px hero) · **flat EDGE BOARD** (`8a24ac9` — Design's core mobile screen: NEW flat edge-ranked IA, `slateAdapter.flattenToEdgeBoard`+`MobileEdgeBoard`, shown <768px, cards ≥768px; threaded edge_pct through the pipeline) · **M4 structural locks** (`6bc9093`).
|
||
- **Key finding:** the 19 mobile screens were LARGELY already built (pages built to the SAME DS0-DS5/§7 contracts Design's mobile follows) → they already match structurally + stack on mobile. The genuinely-new work was the flat board (done) + narrow chip/hero polish.
|
||
REMAINING (all VISUAL, for the master audit): per-screen mobile polish of the ~17 already-built screens (blind, mostly Design-aligned) · M2 billboard crop-ratio verification (OG routes exist, colors fixed). Test baseline: 266 suites / 3214.
|
||
|
||
### 🔴 MASTER-AUDIT UNVERIFIED LIST (every visual change this train — Kev's one end-of-session audit)
|
||
1. **Palette** (biggest): grade B-white/C-grey/red-#FF4757 + full surface/text/border ramp — every surface inherits.
|
||
2. **Archetype marks + colors** — 44 archetypes render Design's real 83-mark set.
|
||
3. **Flat EDGE BOARD** at 390px — the core mobile screen (rows/opacity-ramp/chips/hero-edge%).
|
||
4. **Grade-reveal mobile** (74px hero + team chip) · **app-bar clock** (Hybrid) · **M1a header collapse**.
|
||
5. **TeamChip** on board rows / grade reveal / pitcher-vs (real logo at 10-12px).
|
||
6. **OG billboards** (ShareCard/player/u) grade-B white + text #F0F0F0.
|
||
|
||
## SHIPPED — HEARTBEAT + CADENCE + QUOTA-GUARD (2026-07-15, on main, deployed)
|
||
Three ships, each green + deployed. Standing authority is now PERMANENT full autonomy (three-gate floor: tests green, web build exit 0, post-deploy fingerprint of NEW code).
|
||
- **`7712f0a` Heartbeat honesty (fingerprint-verified live):** the "frozen snapshot / STALE 8h" was a MEASUREMENT bug, not a dead pipeline. `updated_at` = grade-LOCK time (advances 5×/day by design; grades never change in-game); the SYNC badge measured the 20-min `expected_interval_s` against it → structurally STALE. `refreshed_at` (the real 20-min intraday heartbeat) was written to Redis but NEVER serialized. FIX: full snapshot seeds `refreshed_at`, `/api/snapshot/summary` + GET `/:sport` expose it, `LiveLayer` badge measures from it. Fingerprint PROVED the pipeline was alive all along (`refreshed_at` advancing 20:20→20:40→21:00→22:00; the 22:00 full slot fired → scheduler healthy, re-armed fine after the 19:37 restart).
|
||
- **`4cd933d` + `2d413cf` Per-sport cadence (Job 1):** `src/config/sportCadence.js` (config-over-constants). REALITY-CORRECTED: mlb/nba/**wnba** all keep the full grid `14/19/22/1/3` (PropLine 9k/day = cheap; WNBA games span afternoon→late-evening, so 14 UTC/10am ET catches afternoon-game props — the earlier "drop it as waste" was wrong, corrected by observing 2 finished afternoon WNBA games on Jul 15). **Soccer alone is trimmed** to `14/19` + NO intraday — the genuine win, protecting the scarce odds-api key. Scheduler fires at HOURS_UTC and grades only `sportsForHour(h)`; only `intradaySports()` refresh.
|
||
- **`2d413cf` Quota guard (the real finding):** odds-api quota was EXHAUSTED 0/500 mid-cycle, unpaged. Root cause: `futuresService` (the only regular burner) used raw axios, BYPASSING the gateway → never hit `recordCall` (the ONLY place the WARN/BLOCK pager fires) → the counter reached 100% via silent header-sync. FIX: futures now routes through `gateway.fetch('odds-api', …)` (counted + blocked); a RESERVE floor (`ODDS_API_RESERVE`=50) refuses DISCRETIONARY calls (futures/soccer) while `remaining ≤ reserve` so they can NEVER starve MLB's essential prop-backup; `quotaTracker.syncFromHeaders` (authoritative) now ALSO fires the once-per-period alert; `POST /api/internal/quota/test-alert` test-fires the pager end-to-end. **Fingerprint caveat:** backend-internal, no unauthenticated public surface (`/api/internal/*` 401s for any path — auth runs before route-match); verify via authenticated test-alert (200=new/404=old) with the internal key.
|
||
- **odds-api 0/500 → BOTH soccer props AND futures are blocked on QUOTA, not code.** Kev's ruling: HOLD THE LINE (don't pay; zero-out-of-pocket). Soccer stays honest-empty; cadence is ready for the reset. Env: keep `API_FOOTBALL_KEY` (code reads it), delete inert `API_FOOTBALL_KEY2` (Kev handling). See memory [[odds-api-quota-soccer-blocked]]. Test baseline: 264 suites / 3195.
|
||
|
||
## 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 —
|