DS2: Dashboard Slate Rebuild — one hero, pending collapse, never-empty hero

DESIGN-SPEC Parts 3 + 6 (audit #1, #13, #14). The founder's named #1 rebuild.

slateAdapter.js — the testable engine:
- selectTopGrades: rank tonight's grades by tier → confidence → edge so the
  row varies on a real signal, not identical-weight noise (#13).
- buildHeroReceipts: yesterday's PROVEN A-tier settled HITS (misses excluded),
  carrying the real result — the never-empty proof source (#1, Part 6).
- heroFallbackState: tonight wins, else receipts, else empty.
- pendingSummary: collapse an all-awaiting card's six "Grades post …" rows to
  ONE line count (#14).
- topReadForCard: the single best live graded read to promote (#2).

GameCard.tsx — ONE bold hero per card (large mono/tabular grade + player, rest
demoted); all-awaiting cards render one "N props pending · grade ~X ET" line via
nextRunLabelET instead of repeated filler. Real team logos + team-colored accent
already lead the card (DS0) — preserved.

dashboard/page.tsx — Top grades tonight ranked via selectTopGrades (+ % CONF the
varying signal); when tonight is empty, fetch /api/ledger/model and fall back to
yesterday's PROVEN A-tier receipts (✓ HIT + actual + CLV) so first paint always
proves the model. Honest nextRunLabelET copy kept for the truly-empty case (QA.22).

Tests: tests/unit/ds2Dashboard.test.js (21) — pure-fn + source assertions,
fail-before / pass-after. Full suite 237 suites / 2863 tests green (+21).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kev
2026-07-13 00:07:06 -04:00
parent cf91c04e90
commit fe294a5de3
4 changed files with 531 additions and 48 deletions
+38 -1
View File
@@ -10,6 +10,8 @@ import TeamLogo from '@/components/vyndr/TeamLogo';
import { accentColor } from '@/lib/teamMeta';
import { playerHref } from '@/lib/playerHref';
import { isPreferredBook } from '@/lib/books';
import { pendingSummary, topReadForCard } from '@/lib/slateAdapter';
import { nextRunLabelET } from '@/lib/pipelineSchedule';
import { useParlay, legKey } from '@/contexts/ParlayContext';
export interface GameLine {
@@ -182,6 +184,12 @@ export default function GameCard({ game: g, onAddParlay, onOpen, preferredBooks
const [linesExpanded, setLinesExpanded] = useState(false);
const collapsed = useMemo(() => collapseStrips(g.playerStrips || []), [g.playerStrips]);
const stripsToRender = showAllReads ? collapsed.sorted : collapsed.visible;
// DS2 (#2) — the ONE bold hero per card: the single best live graded read,
// promoted large + mono + tabular. Everything else in the strips below stays
// demoted context. (#14) — when the card has NO graded reads yet, the awaiting
// props collapse to ONE pending line instead of six identical rows.
const topRead = useMemo(() => topReadForCard(g.playerStrips || []), [g.playerStrips]);
const pending = useMemo(() => pendingSummary(g.playerStrips || []), [g.playerStrips]);
// A1 S11 — LIVE SLATE MODE: any strip prop carrying live tracking shows the
// once-per-card label. Grades locked pre-game NEVER change in-game.
const isTracking = useMemo(
@@ -321,7 +329,36 @@ export default function GameCard({ game: g, onAddParlay, onOpen, preferredBooks
</span>
)}
</SectionHead>
{g.playerStrips && g.playerStrips.length > 0 ? (
{/* DS2 (#2) — ONE bold hero: the card's best read, large + mono +
tabular. The failure mode was every value at identical weight; this
promotes the single number that matters and demotes the rest. */}
{topRead && !pending && (
<div
style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 11, padding: '10px 12px', borderRadius: 9, background: 'var(--bg-2)', border: '1px solid var(--border-hi)' }}
>
<GradeBadge grade={topRead.grade} size={48} glow />
<div style={{ minWidth: 0, flex: 1 }}>
<div style={{ fontSize: 15, fontWeight: 800, letterSpacing: '-0.01em', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{topRead.player}</div>
<div className="mono" style={{ fontSize: 12, color: 'var(--text-1)', marginTop: 3, fontVariantNumeric: 'tabular-nums' }}>
{topRead.stat} {String(topRead.side).toUpperCase().startsWith('U') ? 'u' : 'o'}{topRead.line}
{topRead.team ? <span style={{ color: 'var(--text-2)' }}> · {topRead.team}</span> : null}
</div>
</div>
<span className="mono" style={{ fontSize: 9, fontWeight: 800, letterSpacing: '0.1em', color: 'var(--text-2)', flexShrink: 0 }}>TOP READ</span>
</div>
)}
{/* DS2 (#14) — collapse the dead "Grades post …" repetition. An
all-awaiting card renders ONE compact line, not six identical rows. */}
{pending ? (
<div className="mono" style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12, color: 'var(--text-1)', flexWrap: 'wrap' }}>
<span style={{ fontWeight: 800, color: 'var(--text-0)', fontVariantNumeric: 'tabular-nums' }}>{pending.count}</span>
<span>props pending</span>
<span style={{ color: 'var(--text-2)' }}>·</span>
<span style={{ color: 'var(--text-2)' }}>{pending.players} player{pending.players === 1 ? '' : 's'}</span>
<span style={{ color: 'var(--text-2)' }}>·</span>
<span style={{ color: 'var(--text-2)', fontStyle: 'italic' }}>{nextRunLabelET() ? `grade ${nextRunLabelET()}` : 'grade on the next pipeline run'}</span>
</div>
) : g.playerStrips && g.playerStrips.length > 0 ? (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
{stripsToRender.map((ps, i) => (
<StatStrip