Files
vyndr/tests/unit/ds4Billboards.test.js
T
builtbykev f5156dd16d Un-claim CLV on the public profile; un-fabricate player-page FORM
Two Truth-Law fixes found by auditing the product logged-out.

FIX 1 — /u/[handle] claimed a "CLV-verified record" with "closing-line
value included" while ZERO closing-line value renders there. Verified
live: GET /api/profiles/vyndr returns beat_close_pct null (gated behind
CLV_CAPTURE_RELIABLE, unset while C4 is open). Eight instances found —
two of them (the OG + portrait "CLV-VERIFIED RECORD · 30D" eyebrows)
only by the post-removal residual sweep; two more printed the claim in
exactly the no-record branch.

Copy now describes what the page shows. The gated CLV-VERIFIED badge and
the BEAT CLOSE figure are removed from the public profile, OG card and
portrait card. DISPLAY ONLY: beat_close_pct, clvCaptureReliable() and
the whole CLV data path are untouched, and the earned directional badge
stays Analyst+Desk. The claim returns when CLV genuinely renders here.

Also fixes the doubled "· VYNDR · VYNDR" title (layout's '%s · VYNDR'
template already supplies the suffix); verified on composed output by
serving the build and reading the real HTML, not on source.

FIX 2 — the player page's FORM was `70 + 4 × (count of tonight's graded
props)`. Nothing on the HTTP path ever sets stats.form, so that fallback
WAS the live number: Josh Bell's "74" is 70 + 4×1 prop, confirmed
against his live payload. MATCHUP was gradeFromForm(that number), with a
hardcoded 'B' on the no-archetype branch — both fabricated letters with
no opponent input on the path. Systemic: buildIntel is the unconditional
path for every player and sport.

FORM and MATCHUP now render "—" (kind 'plain', so no bar width or colour
is computed off a null). gradeFromForm is deleted and the prop count is
no longer passed into buildIntel. computeFormScore's hardcoded 75 now
returns undefined. Induced across MLB/NBA/WNBA: all render cleanly, and
real values (USAGE 3.6 AB/G, REST B2B) still render.

Neither form value feeds the grade — engine1 reads raw l5_avg/l20_avg
against the line and never a form key; buildIntelFields decorates the
already-graded object. Grade inputs are byte-identical.

Held (needs a per-sport headline-stat design call): a real player-level
form metric + label disambiguation.

Tests 3491 passed / 289 suites, web build exit 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VCNgGSt5qvcLxaeQqa7Zpj
2026-07-20 17:05:04 -04:00

186 lines
8.1 KiB
JavaScript

// DS4 (Design v2) — the BILLBOARDS. Screenshot-first surfaces (the timeline is
// customer #1): the STREAKS row, the grade reveal, the CLV reframe, and the /u
// public profile. The .tsx surfaces are asserted against source (plain-JS Jest,
// no TS transform) — same pattern as publicProfilePage / vyndrParityQA. The
// CLV flat-vs-spread decision is a pure fn and is exercised directly.
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');
// ── 1. CLV reframe — the pure flat/spread decision (#16) ──────────────────
describe('clvDisplay — flat CLV says so, spread shows bars', () => {
const { clvMode, isFlatClv, CLV_FLAT_LINE } = require('../../web/src/lib/clvDisplay');
test('near-flat (73/74 at the close) is FLAT, not a broken histogram', () => {
const dist = [
{ label: '[-.5,0)', side: 'faded', count: 0 },
{ label: '0', side: 'flat', count: 73 },
{ label: '(0,.5]', side: 'beat', count: 1 },
];
expect(clvMode(dist).mode).toBe('flat');
expect(isFlatClv(dist)).toBe(true);
});
test('real dispersion is SPREAD (the bars are meaningful)', () => {
const dist = [
{ label: '[-1,-.5)', side: 'faded', count: 8 },
{ label: '[-.5,0)', side: 'faded', count: 6 },
{ label: '0', side: 'flat', count: 5 },
{ label: '(0,.5]', side: 'beat', count: 9 },
{ label: '(.5,1]', side: 'beat', count: 12 },
];
expect(clvMode(dist).mode).toBe('spread');
expect(isFlatClv(dist)).toBe(false);
});
test('null / empty / all-zero → none (renders nothing, never errored)', () => {
expect(clvMode(null).mode).toBe('none');
expect(clvMode([]).mode).toBe('none');
expect(clvMode([{ side: 'flat', count: 0 }, { side: 'beat', count: 0 }]).mode).toBe('none');
});
test('the flat line is confident + VOICE-compliant (no jargon, no hype)', () => {
expect(CLV_FLAT_LINE).toMatch(/grade the outcome, not the close/);
expect(CLV_FLAT_LINE).not.toMatch(/!/);
expect(CLV_FLAT_LINE.toLowerCase()).not.toMatch(/bayesian|regression|guarantee/);
});
test('the ledger MODEL tab renders the flat line, not a histogram, when flat', () => {
const src = read('app/ledger/page.tsx');
expect(src).toContain("import { clvMode, CLV_FLAT_LINE }");
expect(src).toMatch(/mode === 'flat'/);
expect(src).toContain('CLV_FLAT_LINE');
// bars still exist for the spread branch
expect(src).toContain('CLV DISTRIBUTION');
});
});
// ── 2. STREAKS row — the Bloomberg alert (#2) ─────────────────────────────
describe('StreaksPanel — one hero figure, real identity, one accent', () => {
const src = read('components/StreaksPanel.tsx');
test('uses the real PlayerAvatar entity (not a bare <img>)', () => {
expect(src).toContain('import PlayerAvatar');
expect(src).toContain('<PlayerAvatar');
expect(src).not.toMatch(/<img\b/);
});
test('the streak LENGTH is the hero — the largest font in the row', () => {
// heroNum renders currentStreak and is the biggest fontSize; the player
// name is demoted well below it.
expect(src).toMatch(/heroNum[\s\S]*fontSize:\s*38/);
expect(src).toContain('{s.currentStreak}');
const heroSize = Number((src.match(/heroNum:[^}]*fontSize:\s*(\d+)/) || [])[1]);
const nameSize = Number((src.match(/playerName:[^}]*fontSize:\s*(\d+)/) || [])[1]);
expect(heroSize).toBeGreaterThan(nameSize);
expect(heroSize).toBeGreaterThanOrEqual(32);
});
test('the hero number is mono + tabular (data grammar)', () => {
expect(src).toMatch(/heroNum:[\s\S]*fontVariantNumeric:\s*'tabular-nums'/);
});
test('carries the muted "built vs" lens context', () => {
expect(src).toContain('built vs');
expect(src).toContain('builtVs');
});
test('ONE severity accent — step-up amber / step-down green', () => {
expect(src).toContain("diff === 'step up'");
expect(src).toContain('var(--amber');
expect(src).toContain("diff === 'step down'");
expect(src).toContain('var(--g-a');
});
test('the grade badge is tier-gated (the READ is the paid layer)', () => {
expect(src).toMatch(/tier === 'analyst' \|\| tier === 'desk'/);
expect(src).toContain('<GradeBadge');
});
});
// ── 3. Grade reveal — sign-colored edge (#3, the color contract) ──────────
describe('GradeResultCard — the letter is hero, edge is sign-colored', () => {
const src = read('components/vyndr/GradeResultCard.tsx');
test('a negative edge NEVER renders green — negative = var(--miss)', () => {
// DS3 superseded DS4's local const with the centralized colorContract
// enforcer — same semantics (negative → var(--miss)), now via the shared
// helper. Both the confidence strip and the projection row route edge
// through edgeColor(d.edge), never a hardcoded green.
expect(src).toMatch(/import \{ edgeColor.*\} from '@\/lib\/colorContract'/);
expect(src).toContain('color: edgeColor(d.edge)');
expect(src).toMatch(/d\.edge != null \? edgeColor\(d\.edge\) : 'var\(--text-2\)'/);
});
test('the grade LETTER is still the hero (largest element)', () => {
expect(src).toMatch(/grade-hero[\s\S]*fontSize: compact \? 92 : 116/);
});
test('the MODEL/LINE/EDGE row is mono + tabular (demoted data)', () => {
expect(src).toMatch(/fontSize: 19[\s\S]*fontVariantNumeric: 'tabular-nums'/);
});
test('reveal choreography is reduced-motion safe', () => {
const proc = read('components/vyndr/ProcessingGrade.tsx');
expect(proc).toContain("prefers-reduced-motion: reduce");
expect(proc).toMatch(/setPhase\('card'\)/);
});
});
// ── 4. /u public profile — the shareable record billboard (#4) ────────────
describe('PublicProfile — the record hero (Session 65: no CLV claim)', () => {
const src = read('app/u/[handle]/PublicProfile.tsx');
test('the hit rate is the bold hero figure (large mono tabular)', () => {
expect(src).toMatch(/fontSize: 56[\s\S]*fontVariantNumeric: 'tabular-nums'/);
expect(src).toContain('{agg.hit_pct}%');
expect(src).toContain('HIT RATE');
});
// Session 65 — UN-CLAIM. The beat-close figure and the ✓ CLV-VERIFIED badge
// are GONE from this public surface: both read beat_close_pct, derived from
// the legacy overwritable closing_line (C4 open). Display removal only — the
// aggregate and its CLV_CAPTURE_RELIABLE gate in ledgerService are untouched.
test('no closing-line claim renders on the public record hero', () => {
expect(src).not.toContain('{agg.beat_close_pct}%');
expect(src).not.toContain('BEAT CLOSE');
expect(src).not.toContain('CLV-VERIFIED');
expect(src).not.toMatch(/clvVerified/);
});
test('still honors the n-gate: RECORD BUILDING under min_sample', () => {
expect(src).toContain('RECORD BUILDING');
expect(src).toMatch(/settled >= minSample/);
});
test('renders real player identity (PlayerAvatar entity)', () => {
expect(src).toContain('import PlayerAvatar');
expect(src).toContain('<PlayerAvatar');
});
});
// ── 5. /u OG image — the record at social crop ────────────────────────────
describe('/u opengraph-image — real record at 1200x630 social crop', () => {
const src = read('app/u/[handle]/opengraph-image.tsx');
test('social crop size is preserved (1200x630)', () => {
expect(src).toMatch(/size = \{ width: 1200, height: 630 \}/);
});
test('renders the real hit% when the record is published, and claims no CLV', () => {
expect(src).toContain('{rec.hit_pct}%');
// Session 65 — beat-close% + the CLV-VERIFIED eyebrow are un-claimed here.
expect(src).not.toContain('{rec.beat_close_pct}%');
expect(src).not.toMatch(/CLV-VERIFIED|CLV-verified/);
expect(src).toContain('SETTLED RECORD · 30D');
});
test('falls back gracefully to an honest tagline when there is no record', () => {
expect(src).toContain('Every settled read · wins and misses · nothing curated');
});
});