From cb3237cdce4f81378304ef0184dab4a0c75bb7a5 Mon Sep 17 00:00:00 2001 From: Kev Date: Sat, 18 Jul 2026 01:42:16 -0400 Subject: [PATCH] =?UTF-8?q?Item=206=20=E2=80=94=20Desk=20showcase=20render?= =?UTF-8?q?s=20REAL=20data=20(or=20hides),=20kills=20the=20mocked=20ladder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pricing Desk showcase hardcoded an alt-line ladder (1.5 A +7.1% / 2.5 A+ +11.4% / 4.5 C -3.8%), QUARTER-KELLY 2.4%, and PARLAY φ 0.34 — a mocked demo selling something we weren't proving. - deskShowcaseService reads the pre-graded snapshot for a real A/B prop's alt-line ladder (prefers the one with the most grade variation — the most compelling real example). Edge per rung shows only when it's a plausible market value; the inflated (model-line)/line artifact on small lines is guarded to "—" rather than shown as a fake +91%. - PARLAY φ is now REAL: the model's same-team correlation (0.34, mirroring the frontend parlayMath team constant) computed for TWO REAL same-team legs, named. No real same-team pair on the board → the tile hides, never an invented number. - QUARTER-KELLY tile is REMOVED: the snapshot has no odds, so a real quarter-Kelly % can't be computed here — a fabricated 2.4% is worse than nothing. Kelly stays a real in-app Desk feature; the showcase just doesn't fake it. - DeskShowcase is now a client component fetching /api/desk-showcase; when the board has no real ladder the whole visuals column hides (real-or-hidden, same law as the hero). The pitch copy is unchanged. 5 service tests. Change-affected suites green, web build exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.js | 1 + src/routes/deskShowcase.js | 26 +++++ src/services/deskShowcaseService.js | 98 +++++++++++++++++++ tests/unit/deskShowcaseService.test.js | 63 ++++++++++++ web/src/app/api/desk-showcase/route.ts | 21 ++++ web/src/app/pricing/DeskShowcase.tsx | 129 +++++++++++++++---------- 6 files changed, 287 insertions(+), 51 deletions(-) create mode 100644 src/routes/deskShowcase.js create mode 100644 src/services/deskShowcaseService.js create mode 100644 tests/unit/deskShowcaseService.test.js create mode 100644 web/src/app/api/desk-showcase/route.ts diff --git a/src/app.js b/src/app.js index ec23499..d0d8a23 100644 --- a/src/app.js +++ b/src/app.js @@ -217,6 +217,7 @@ app.use('/api/internal', internalRoutes); app.use('/api/partners', require('./routes/partners')); app.use('/api/founders', require('./routes/founders')); app.use('/api/hero-prop', require('./routes/heroProp')); +app.use('/api/desk-showcase', require('./routes/deskShowcase')); // Session 10 — Sentry's Express error handler catches uncaught // errors from every route mounted above. Must come AFTER routes but diff --git a/src/routes/deskShowcase.js b/src/routes/deskShowcase.js new file mode 100644 index 0000000..d7bbf4b --- /dev/null +++ b/src/routes/deskShowcase.js @@ -0,0 +1,26 @@ +'use strict'; + +/** + * GET /api/desk-showcase (Truth-Everywhere Part 2, item 6) — REAL data for the + * pricing Desk showcase (alt-line ladder + same-team correlation). Public, + * cache-only. { available:false } when the board has nothing → the visuals hide. + */ +const express = require('express'); +const { createRateLimit } = require('../middleware/rateLimit'); +const deskShowcaseService = require('../services/deskShowcaseService'); + +const router = express.Router(); +router.use(createRateLimit({ windowMs: 60_000, max: 60 })); + +router.get('/', async (req, res) => { + try { + const data = await deskShowcaseService.getDeskShowcase({}); + res.set('Cache-Control', 'public, max-age=300'); + return res.json(data); + } catch (err) { + console.error('[desk-showcase]', err.message); + return res.status(200).json({ available: false }); + } +}); + +module.exports = router; diff --git a/src/services/deskShowcaseService.js b/src/services/deskShowcaseService.js new file mode 100644 index 0000000..ed8fa8d --- /dev/null +++ b/src/services/deskShowcaseService.js @@ -0,0 +1,98 @@ +'use strict'; + +/** + * deskShowcaseService (Truth-Everywhere Part 2, item 6) — REAL data for the + * pricing-page Desk showcase. The ladder/Kelly/phi were hardcoded mocks; this + * feeds the flagship demo from the pre-graded snapshot, or returns nothing so + * the visuals HIDE (real-or-hidden, same law as the hero). No grading, no + * credits — cache reads only. + * + * - ladder: a real A/B graded prop's alt-line ladder (line + grade; the edge + * is shown only when it's a plausible market value, else omitted — the + * (model-line)/line metric is huge on 0.5-lines and would look fake). + * - parlay: the model's real same-team correlation (0.34, mirrors the frontend + * parlayMath team constant) computed for TWO REAL same-team A/B legs. No + * real pair on the board → null (hidden), never an invented number. + * - kelly: null — the snapshot carries no odds, so a real quarter-Kelly % can't + * be computed here. The tile hides rather than show a fabricated 2.4%. + */ + +const DEFAULT_SPORTS = ['nba', 'wnba', 'mlb', 'soccer']; +const TEAM_CORRELATION = 0.34; // mirrors web parlayMath: same-team pairwise phi +const SANE_EDGE_MAX = 40; // beyond this the (model-line)/line value isn't a market edge + +const isAB = (g) => /^[AB]/.test(String(g || '').trim().toUpperCase()); +const distinctGrades = (ladder) => new Set((ladder || []).map((r) => r.grade)).size; + +function rungsOf(alt) { + return (alt || []) + .filter((r) => r && Number.isFinite(Number(r.line)) && r.grade) + .map((r) => { + const edge = Number(r.edge_pct); + return { + line: Number(r.line), + grade: r.grade, + base: !!r.base, + // guard the small-line artifact: show an edge only when it's plausible + edge: Number.isFinite(edge) && Math.abs(edge) <= SANE_EDGE_MAX ? Math.round(edge * 10) / 10 : null, + }; + }) + .sort((a, b) => a.line - b.line); +} + +async function getDeskShowcase(deps = {}) { + const cacheGet = deps.cacheGet || require('../utils/redis').cacheGet; + const sports = deps.sports || DEFAULT_SPORTS; + + const ab = []; + for (const sport of sports) { + let grades = null; + const snap = await cacheGet(`snapshot:${sport}:latest`); + if (snap && Array.isArray(snap.grades)) grades = snap.grades; + else { + const env = await cacheGet(`grades:${sport}`); + if (env && Array.isArray(env.grades)) grades = env.grades; + } + for (const g of grades || []) { + if (!g || g.insufficient_data || !isAB(g.grade)) continue; + ab.push({ g, sport }); + } + } + + // Ladder: prefer the prop whose ladder shows the MOST grade variation (the + // most compelling REAL example — "A here, C there"), then longest ladder. + let best = null, bestScore = -1; + for (const { g, sport } of ab) { + const rungs = rungsOf(g.alt_lines); + if (rungs.length < 2) continue; + const score = distinctGrades(rungs) * 100 + rungs.length; + if (score > bestScore) { bestScore = score; best = { g, sport, rungs }; } + } + + if (!best) return { available: false }; + + const ladder = { + player: best.g.player_name || best.g.player || null, + stat_type: best.g.stat_type || best.g.stat || null, + sport: best.sport, + rungs: best.rungs, + }; + + // Parlay phi: two REAL A/B legs on the same (non-null) team. + let parlay = null; + const byTeam = {}; + for (const { g } of ab) { + const t = g.team ? String(g.team) : null; + const name = g.player_name || g.player; + if (!t || !name) continue; + (byTeam[t] = byTeam[t] || []).push(name); + } + for (const [team, names] of Object.entries(byTeam)) { + const uniq = [...new Set(names)]; + if (uniq.length >= 2) { parlay = { value: TEAM_CORRELATION, legs: [uniq[0], uniq[1]], team }; break; } + } + + return { available: true, ladder, parlay, kelly: null }; +} + +module.exports = { getDeskShowcase, __internals: { rungsOf, isAB, SANE_EDGE_MAX, TEAM_CORRELATION } }; diff --git a/tests/unit/deskShowcaseService.test.js b/tests/unit/deskShowcaseService.test.js new file mode 100644 index 0000000..57ce518 --- /dev/null +++ b/tests/unit/deskShowcaseService.test.js @@ -0,0 +1,63 @@ +'use strict'; + +// Item 6 (Truth-Everywhere Part 2) — the Desk showcase renders REAL snapshot +// data (alt-line ladder + same-team correlation) or hides. No mocked numbers. + +const { getDeskShowcase, __internals } = require('../../src/services/deskShowcaseService'); + +const cacheFrom = (map) => async (k) => (k in map ? map[k] : null); +const grade = (o) => ({ + player_name: o.player, stat_type: o.stat || 'hits', grade: o.grade, + team: o.team || null, alt_lines: o.alt || [], +}); + +describe('getDeskShowcase', () => { + test('renders a real A/B ladder; inflated edges are guarded to null', async () => { + const cacheGet = cacheFrom({ 'snapshot:mlb:latest': { grades: [ + grade({ player: 'Vary', stat: 'strikeouts', grade: 'A', alt: [ + { line: 4.5, grade: 'A', edge_pct: 8.2 }, { line: 6.5, grade: 'B', edge_pct: 3.1, base: true }, { line: 8.5, grade: 'C', edge_pct: 91.3 }, + ] }), + ] } }); + const out = await getDeskShowcase({ cacheGet, sports: ['mlb'] }); + expect(out.available).toBe(true); + expect(out.ladder.player).toBe('Vary'); + expect(out.ladder.rungs.map((r) => r.grade)).toEqual(['A', 'B', 'C']); // sorted by line + expect(out.ladder.rungs[0].edge).toBe(8.2); // sane edge kept + expect(out.ladder.rungs[2].edge).toBeNull(); // 91.3% guarded (artifact, not a market edge) + expect(out.kelly).toBeNull(); // no odds → never a fabricated % + }); + + test('prefers the ladder with the MOST grade variation (best real example)', async () => { + const cacheGet = cacheFrom({ 'snapshot:mlb:latest': { grades: [ + grade({ player: 'Flat', grade: 'B', alt: [{ line: 0.5, grade: 'B' }, { line: 1.5, grade: 'B' }] }), + grade({ player: 'Varies', grade: 'A', alt: [{ line: 0.5, grade: 'A' }, { line: 1.5, grade: 'C' }] }), + ] } }); + const out = await getDeskShowcase({ cacheGet, sports: ['mlb'] }); + expect(out.ladder.player).toBe('Varies'); + }); + + test('parlay φ is REAL — the model correlation for two real same-team legs', async () => { + const cacheGet = cacheFrom({ 'snapshot:mlb:latest': { grades: [ + grade({ player: 'Alice', grade: 'A', team: 'NYY', alt: [{ line: 0.5, grade: 'A' }, { line: 1.5, grade: 'B' }] }), + grade({ player: 'Bob', grade: 'B', team: 'NYY', alt: [{ line: 0.5, grade: 'B' }, { line: 1.5, grade: 'C' }] }), + ] } }); + const out = await getDeskShowcase({ cacheGet, sports: ['mlb'] }); + expect(out.parlay).toEqual({ value: 0.34, legs: ['Alice', 'Bob'], team: 'NYY' }); + }); + + test('no same-team pair → parlay null (never an invented correlation)', async () => { + const cacheGet = cacheFrom({ 'snapshot:mlb:latest': { grades: [ + grade({ player: 'Solo', grade: 'A', team: 'NYY', alt: [{ line: 0.5, grade: 'A' }, { line: 1.5, grade: 'C' }] }), + ] } }); + const out = await getDeskShowcase({ cacheGet, sports: ['mlb'] }); + expect(out.parlay).toBeNull(); + }); + + test('no A/B ladder anywhere → { available:false } (visuals hide)', async () => { + const cacheGet = cacheFrom({ 'snapshot:mlb:latest': { grades: [ + grade({ player: 'Weak', grade: 'D', alt: [{ line: 0.5, grade: 'D' }] }), + ] } }); + const out = await getDeskShowcase({ cacheGet, sports: ['mlb'] }); + expect(out).toEqual({ available: false }); + }); +}); diff --git a/web/src/app/api/desk-showcase/route.ts b/web/src/app/api/desk-showcase/route.ts new file mode 100644 index 0000000..854183a --- /dev/null +++ b/web/src/app/api/desk-showcase/route.ts @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server'; + +export const dynamic = 'force-dynamic'; + +const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3000'; + +/** + * Desk-showcase proxy (item 6) — real alt-line ladder + same-team correlation + * from the snapshot. Any failure → { available:false } so the visuals hide. + */ +export async function GET() { + try { + const upstream = await fetch(`${BACKEND_URL}/api/desk-showcase`, { + method: 'GET', headers: { Accept: 'application/json' }, cache: 'no-store', + }); + const data = await upstream.json().catch(() => ({ available: false })); + return NextResponse.json(data, { status: 200, headers: { 'Cache-Control': 'public, s-maxage=300, stale-while-revalidate=60' } }); + } catch { + return NextResponse.json({ available: false }, { status: 200 }); + } +} diff --git a/web/src/app/pricing/DeskShowcase.tsx b/web/src/app/pricing/DeskShowcase.tsx index 31c0b8f..c284536 100644 --- a/web/src/app/pricing/DeskShowcase.tsx +++ b/web/src/app/pricing/DeskShowcase.tsx @@ -1,32 +1,64 @@ +'use client'; + +import { useEffect, useState } from 'react'; import SectionHead from '@/components/vyndr/SectionHead'; /** - * DeskShowcase (DS5 · DESIGN-SPEC Part 6, #8) — the Desk-as-hero story that - * sits ABOVE the pricing grid. Its job is the founder's one line: make $44.99 - * feel impossibly low for a professional terminal ("how is this only $44.99" - * IS the conversion event). Balanced two-column layout — the pitch on the left, - * REAL feature visuals on the right (kills the dead right-half, audit #8). The - * single primary CTA scrolls to the grid where the real Stripe checkout lives - * (checkout wiring untouched). - * - * Server component — no interactivity here; the CTA is an in-page anchor and the - * feature visuals are static, tokenized, mono-for-data mock readouts. + * DeskShowcase (DS5 · DESIGN-SPEC Part 6, #8) — the Desk-as-hero story above the + * pricing grid. The pitch (left) is copy; the feature visuals (right) are now + * REAL data (Truth-Everywhere Part 2, item 6), fetched from /api/desk-showcase: + * - ALT LINE LADDER: a real graded prop's ladder (line + grade; edge only when + * it's a plausible market value). + * - PARLAY φ: the model's real same-team correlation, computed for two REAL + * same-team legs (named). + * The old hardcoded ladder / QUARTER-KELLY 2.4% / φ 0.34 mocks are gone. When + * the board has no real ladder, the visuals HIDE (real-or-hidden) — the Kelly + * tile is dropped entirely (the snapshot has no odds to size from honestly). */ -// A real alt-line-ladder rung (the Desk exclusive) — line + locked grade + edge. -function Rung({ line, grade, edge, base }: { line: string; grade: string; edge: string; base?: boolean }) { - const gradeCol = grade.startsWith('A') ? 'var(--g-a)' : grade.startsWith('B') ? 'var(--text-0)' : 'var(--amber)'; - const edgeCol = edge.startsWith('+') ? 'var(--g-a)' : edge.startsWith('-') ? 'var(--miss)' : 'var(--text-2)'; +interface Rung { line: number; grade: string; edge: number | null; base?: boolean } +interface Showcase { + available: boolean; + ladder?: { player: string | null; stat_type: string | null; sport?: string; rungs: Rung[] } | null; + parlay?: { value: number; legs: string[]; team?: string } | null; +} + +const STAT_LABEL: Record = { + total_bases: 'TB', home_runs: 'HR', hits: 'H', rbi: 'RBI', runs: 'R', doubles: '2B', + strikeouts: 'K', points: 'PTS', rebounds: 'REB', assists: 'AST', threes: '3PT', +}; +const statLabel = (s?: string | null) => (s ? (STAT_LABEL[s] || s.replace(/_/g, ' ').toUpperCase()) : ''); + +function RungCell({ line, grade, edge, base }: Rung) { + const gradeCol = grade.startsWith('A') ? 'var(--g-a)' : grade.startsWith('B') ? 'var(--text-0)' : grade.startsWith('C') ? 'var(--text-1)' : 'var(--miss)'; + const edgeCol = edge == null ? 'var(--text-2)' : edge >= 0 ? 'var(--g-a)' : 'var(--miss)'; return (
{line}{base ? ' •' : ''}
{grade}
-
{edge}
+
+ {edge == null ? '—' : `${edge >= 0 ? '+' : ''}${edge}%`} +
); } export default function DeskShowcase() { + const [data, setData] = useState(null); + + useEffect(() => { + let alive = true; + fetch('/api/desk-showcase', { cache: 'no-store' }) + .then((r) => r.json()) + .then((d) => { if (alive) setData(d); }) + .catch(() => { if (alive) setData({ available: false }); }); + return () => { alive = false; }; + }, []); + + const ladder = data?.available ? data.ladder : null; + const parlay = data?.available ? data.parlay : null; + const showVisuals = !!ladder && ladder.rungs.length > 0; + return (
@@ -56,46 +88,41 @@ export default function DeskShowcase() {
- {/* RIGHT — real feature visuals (no dead half) */} -
-
- - ALT LINE LADDER DESK - -
- - - - + {/* RIGHT — REAL feature visuals (hidden when the board has none) */} + {showVisuals && ( +
+
+ + ALT LINE LADDER DESK + +
+ {ladder!.player} · {statLabel(ladder!.stat_type)} +
+
+ {ladder!.rungs.map((r) => )} +
-
-
-
- QUARTER-KELLY -
2.4%
-
of bankroll · at -110
-
-
- PARLAY φ -
0.34
-
correlation · same-team legs
-
-
+ {/* PARLAY φ — real, only when two real same-team legs exist */} + {parlay && ( +
+ PARLAY φ +
{parlay.value}
+
+ correlation · {parlay.legs[0]} + {parlay.legs[1]} +
+
+ )} - {/* Wave 4A (Step 4) — this claim is now BACKED by a real feature: the - CONSENSUS vs MODEL strip (components/vyndr/MarketBreadth, fed by - lib/marketBreadth.collectBreadth) ships on the live slate/dashboard, - computing the median book line vs the model's projection. "live - line moves" is the existing snapshot line-deltas / LineSparkline. - No longer an empty promise — do not remove without removing those. */} -
- - - REAL-TIME FEED · consensus vs model, live line moves - + {/* Real-time feed — backed by the live MarketBreadth (consensus vs model). */} +
+ + + REAL-TIME FEED · consensus vs model, live line moves + +
-
+ )}
);