fe294a5de3
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>
195 lines
9.0 KiB
JavaScript
195 lines
9.0 KiB
JavaScript
// DS2 (Design v2) — Dashboard Slate Rebuild. The founder's named #1 rebuild:
|
|
// real entities per card, ONE bold hero, collapse pending-filler (#14), and the
|
|
// NEVER-EMPTY hero (#1, Part 6) — first paint ALWAYS proves the model. The pure
|
|
// engine lives in web/src/lib/slateAdapter.js (unit-tested directly); the .tsx
|
|
// surfaces are asserted against source (plain-JS Jest) — same pattern as
|
|
// ds4Billboards / vyndrParityQA.
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const ROOT = path.join(__dirname, '..', '..');
|
|
const WEB = path.join(ROOT, 'web', 'src');
|
|
const read = (rel) => fs.readFileSync(path.join(WEB, rel), 'utf8');
|
|
const adapter = require('../../web/src/lib/slateAdapter');
|
|
|
|
// ── 1. selectTopGrades — ranking VARIES on a real signal (#13) ─────────────
|
|
describe('selectTopGrades — tier first, then the varying signal (confidence/edge)', () => {
|
|
test('orders by grade tier, then confidence desc, then edge desc', () => {
|
|
const grades = [
|
|
{ player: 'C-low', grade: 'B', confidence: 40, edge: 1 },
|
|
{ player: 'A-hiconf', grade: 'A', confidence: 90, edge: 2 },
|
|
{ player: 'A-loconf', grade: 'A', confidence: 55, edge: 9 },
|
|
{ player: 'Aplus', grade: 'A+', confidence: 10, edge: 0 },
|
|
];
|
|
const out = adapter.selectTopGrades(grades, 10).map((g) => g.player);
|
|
expect(out).toEqual(['Aplus', 'A-hiconf', 'A-loconf', 'C-low']);
|
|
});
|
|
|
|
test('ties on grade break on confidence — rows are NOT identical-order noise', () => {
|
|
const grades = [
|
|
{ player: 'X', grade: 'B', confidence: 30 },
|
|
{ player: 'Y', grade: 'B', confidence: 80 },
|
|
{ player: 'Z', grade: 'B', confidence: 55 },
|
|
];
|
|
expect(adapter.selectTopGrades(grades, 10).map((g) => g.player)).toEqual(['Y', 'Z', 'X']);
|
|
});
|
|
|
|
test('honors the limit and drops gradeless rows; empty in → empty out', () => {
|
|
const grades = [{ grade: 'A', confidence: 1 }, { grade: 'A', confidence: 2 }, { confidence: 9 }];
|
|
expect(adapter.selectTopGrades(grades, 1)).toHaveLength(1);
|
|
expect(adapter.selectTopGrades([], 5)).toEqual([]);
|
|
expect(adapter.selectTopGrades(null, 5)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
// ── 2. buildHeroReceipts — yesterday's A-tier settled HITS (#1) ────────────
|
|
describe('buildHeroReceipts — proven A-tier receipts, misses excluded', () => {
|
|
const rows = [
|
|
{ player_name: 'Aaron Judge', stat: 'total_bases', line: 1.5, side: 'over', grade: 'A+', outcome: 'hit', actual_value: 3, clv_result: 'beat', sport: 'mlb' },
|
|
{ player_name: 'Missed Guy', stat: 'hits', line: 1.5, side: 'over', grade: 'A', outcome: 'miss', actual_value: 0 },
|
|
{ player_name: 'B Guy', stat: 'runs', line: 0.5, side: 'over', grade: 'B', outcome: 'hit', actual_value: 1 },
|
|
{ player_name: 'Aminus Hit', stat: 'rbi', line: 0.5, side: 'over', grade: 'A-', outcome: 'hit', actual_value: 2 },
|
|
];
|
|
|
|
test('keeps only A-family grades that HIT (a miss never becomes proof)', () => {
|
|
const out = adapter.buildHeroReceipts(rows, 8);
|
|
const names = out.map((r) => r.player);
|
|
expect(names).toContain('Aaron Judge');
|
|
expect(names).toContain('Aminus Hit');
|
|
expect(names).not.toContain('Missed Guy'); // A-tier but a MISS
|
|
expect(names).not.toContain('B Guy'); // a hit but not A-tier
|
|
});
|
|
|
|
test('carries the real result (actual + grade) so the receipt is verifiable', () => {
|
|
const judge = adapter.buildHeroReceipts(rows, 8).find((r) => r.player === 'Aaron Judge');
|
|
expect(judge).toMatchObject({ grade: 'A+', outcome: 'hit', actual: 3 });
|
|
});
|
|
|
|
test('best-grade first, honors the limit, empty/absent → []', () => {
|
|
expect(adapter.buildHeroReceipts(rows, 8)[0].grade).toBe('A+');
|
|
expect(adapter.buildHeroReceipts(rows, 1)).toHaveLength(1);
|
|
expect(adapter.buildHeroReceipts([], 5)).toEqual([]);
|
|
expect(adapter.buildHeroReceipts(null, 5)).toEqual([]);
|
|
});
|
|
});
|
|
|
|
// ── 3. heroFallbackState — the NEVER-EMPTY hero engine (#1, Part 6) ────────
|
|
describe('heroFallbackState — tonight wins, else receipts, never empty when proof exists', () => {
|
|
const settled = [{ player_name: 'Judge', stat: 'total_bases', line: 1.5, side: 'over', grade: 'A', outcome: 'hit', actual_value: 3 }];
|
|
|
|
test('tonight has graded props → mode tonight', () => {
|
|
const s = adapter.heroFallbackState([{ player: 'P', grade: 'A', confidence: 70 }], settled, 10);
|
|
expect(s.mode).toBe('tonight');
|
|
expect(s.items).toHaveLength(1);
|
|
});
|
|
|
|
test('tonight EMPTY but yesterday has A-tier hits → mode receipts (proof, not empty)', () => {
|
|
const s = adapter.heroFallbackState([], settled, 10);
|
|
expect(s.mode).toBe('receipts');
|
|
expect(s.items[0].player).toBe('Judge');
|
|
});
|
|
|
|
test('only truly empty when BOTH are absent', () => {
|
|
expect(adapter.heroFallbackState([], [], 10).mode).toBe('empty');
|
|
expect(adapter.heroFallbackState(null, null, 10).mode).toBe('empty');
|
|
});
|
|
});
|
|
|
|
// ── 4. pendingSummary — collapse the dead "Grades post" repetition (#14) ──
|
|
describe('pendingSummary — six pending rows collapse to ONE line', () => {
|
|
test('all-awaiting card → one summary counting props + players', () => {
|
|
const strips = [
|
|
{ player: 'One', props: [{ stat: 'TB', line: 1.5, awaiting: true }, { stat: 'HR', line: 0.5, awaiting: true }] },
|
|
{ player: 'Two', props: [{ stat: 'Hits', line: 0.5, awaiting: true }] },
|
|
];
|
|
const s = adapter.pendingSummary(strips);
|
|
expect(s).not.toBeNull();
|
|
expect(s.count).toBe(3);
|
|
expect(s.players).toBe(2);
|
|
});
|
|
|
|
test('any graded prop present → no collapse (the card has real reads to show)', () => {
|
|
const strips = [
|
|
{ player: 'One', props: [{ stat: 'TB', line: 1.5, grade: 'A' }] },
|
|
{ player: 'Two', props: [{ stat: 'Hits', line: 0.5, awaiting: true }] },
|
|
];
|
|
expect(adapter.pendingSummary(strips)).toBeNull();
|
|
});
|
|
|
|
test('no awaiting props at all → null', () => {
|
|
expect(adapter.pendingSummary([{ player: 'One', props: [] }])).toBeNull();
|
|
expect(adapter.pendingSummary([])).toBeNull();
|
|
expect(adapter.pendingSummary(null)).toBeNull();
|
|
});
|
|
});
|
|
|
|
// ── 5. topReadForCard — the ONE bold hero per card (#2) ────────────────────
|
|
describe('topReadForCard — the single best graded prop leads the card', () => {
|
|
const strips = [
|
|
{ player: 'Mid', team: 'NYY', props: [{ stat: 'Hits', line: 0.5, side: 'O', grade: 'B' }] },
|
|
{ player: 'Best', team: 'BOS', archetype: { primary: 'BOMBER' }, props: [{ stat: 'TB', line: 1.5, side: 'O', grade: 'A+' }] },
|
|
{ player: 'Dead', team: 'LAD', props: [{ stat: 'HR', line: 0.5, side: 'O', grade: 'A+', dead: true }] },
|
|
];
|
|
|
|
test('picks the highest-tier live grade (dead reads never lead)', () => {
|
|
const top = adapter.topReadForCard(strips);
|
|
expect(top).not.toBeNull();
|
|
expect(top.player).toBe('Best');
|
|
expect(top.grade).toBe('A+');
|
|
expect(top.stat).toBe('TB');
|
|
});
|
|
|
|
test('no graded props → null (nothing to promote)', () => {
|
|
expect(adapter.topReadForCard([{ player: 'X', props: [{ stat: 'TB', line: 1, awaiting: true }] }])).toBeNull();
|
|
expect(adapter.topReadForCard([])).toBeNull();
|
|
});
|
|
});
|
|
|
|
// ── 6. GameCard.tsx — one hero + pending collapse + real entities ─────────
|
|
describe('GameCard.tsx wires the DS2 rebuild', () => {
|
|
const src = read('components/vyndr/GameCard.tsx');
|
|
|
|
test('leads with the real team entity (logo) + a team-colored accent edge', () => {
|
|
expect(src).toContain('<TeamLogo');
|
|
expect(src).toContain('accentColor(g.home.abbr');
|
|
expect(src).toContain('borderLeft');
|
|
});
|
|
|
|
test('promotes ONE bold hero read via topReadForCard (large mono/tabular grade)', () => {
|
|
expect(src).toContain('topReadForCard');
|
|
expect(src).toMatch(/fontVariantNumeric:\s*'tabular-nums'/);
|
|
// the hero grade badge is the largest on the card (strips use size 'sm').
|
|
expect(src).toMatch(/size=\{48\}|size="lg"/);
|
|
});
|
|
|
|
test('collapses all-awaiting cards to ONE pending line (kills the #14 repetition)', () => {
|
|
expect(src).toContain('pendingSummary');
|
|
expect(src).toContain('nextRunLabelET');
|
|
expect(src).toContain('props pending');
|
|
});
|
|
});
|
|
|
|
// ── 7. dashboard/page.tsx — the never-empty hero + varying rankings ────────
|
|
describe('dashboard Top grades tonight — proof-first, ranked, honest', () => {
|
|
const src = read('app/dashboard/page.tsx');
|
|
|
|
test('uses the never-empty engine (heroFallbackState / buildHeroReceipts)', () => {
|
|
expect(src).toMatch(/heroFallbackState|buildHeroReceipts/);
|
|
});
|
|
|
|
test('ranks tonight grades by the varying signal (selectTopGrades) and shows confidence', () => {
|
|
expect(src).toContain('selectTopGrades');
|
|
expect(src).toContain('confidence');
|
|
});
|
|
|
|
test('falls back to yesterday PROVEN A-tier receipts (with the settled result)', () => {
|
|
expect(src).toContain('/api/ledger/model');
|
|
expect(src).toMatch(/HIT|PROVEN|YESTERDAY/);
|
|
});
|
|
|
|
test('still keeps the honest schedule-derived waiting copy for the truly-empty case (QA.22)', () => {
|
|
expect(src).toContain('nextRunLabelET');
|
|
});
|
|
});
|