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
This commit is contained in:
Kev
2026-07-20 17:05:04 -04:00
parent ca9ca34cbb
commit f5156dd16d
12 changed files with 286 additions and 83 deletions
+17 -14
View File
@@ -132,7 +132,7 @@ describe('GradeResultCard — the letter is hero, edge is sign-colored', () => {
});
// ── 4. /u public profile — the shareable record billboard (#4) ────────────
describe('PublicProfile — the record hero + CLV-verified badge', () => {
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)', () => {
@@ -141,14 +141,15 @@ describe('PublicProfile — the record hero + CLV-verified badge', () => {
expect(src).toContain('HIT RATE');
});
test('beat-close is the secondary bold figure', () => {
expect(src).toContain('{agg.beat_close_pct}%');
expect(src).toContain('BEAT CLOSE');
});
test('CLV-VERIFIED badge is HONEST — only when closing value is tracked', () => {
expect(src).toContain('✓ CLV-VERIFIED');
expect(src).toMatch(/clvVerified = Boolean\(ready && agg && agg\.beat_close_pct != null\)/);
// 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', () => {
@@ -170,13 +171,15 @@ describe('/u opengraph-image — real record at 1200x630 social crop', () => {
expect(src).toMatch(/size = \{ width: 1200, height: 630 \}/);
});
test('renders the real hit% / beat-close% when the record is published', () => {
test('renders the real hit% when the record is published, and claims no CLV', () => {
expect(src).toContain('{rec.hit_pct}%');
expect(src).toContain('{rec.beat_close_pct}%');
expect(src).toContain('CLV-VERIFIED RECORD');
// 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 the tagline when there is no record', () => {
expect(src).toContain('every settled read · misses included');
test('falls back gracefully to an honest tagline when there is no record', () => {
expect(src).toContain('Every settled read · wins and misses · nothing curated');
});
});
+9
View File
@@ -15,6 +15,15 @@ describe('computeFormScore', () => {
it('undefined when there is no recent average', () => {
expect(computeFormScore({})).toBeUndefined();
});
// Session 65 — UN-FABRICATE. This branch used to `return 75`: a hardcoded
// constant rendered under the FORM label whenever the baseline was missing
// or zero. With no baseline there is no ratio, so the field is absent and
// the card's self-hiding intel section drops the row.
it('undefined (never a hardcoded 75) when the baseline is missing or zero', () => {
expect(computeFormScore({ l5_avg: 12 })).toBeUndefined();
expect(computeFormScore({ l5_avg: 12, l20_avg: 0 })).toBeUndefined();
expect(computeFormScore({ l5_avg: 12, l20_avg: 0, l10_avg: 0 })).toBeUndefined();
});
});
describe('matchupGradeFromRank', () => {
+57
View File
@@ -89,3 +89,60 @@ describe('getLeaders', () => {
expect(await svc.getLeaders('nba', { cacheGet: async () => null })).toEqual([]);
});
});
// ── Session 65 — UN-FABRICATE (Truth Law) ────────────────────────────────
// FORM used to be `70 + 4 × (count of tonight's graded props)` whenever
// `stats.form` was absent — and nothing on the HTTP path ever sets it, so that
// fallback WAS the live number (Josh Bell's "74" = 70 + 4×1 prop). MATCHUP was
// `gradeFromForm(that number)`, with a hardcoded 'B' when no archetype
// resolved: both branches fabricated a letter with zero opponent input.
describe('buildIntel — absent renders absent, never a manufactured number', () => {
const { buildIntel } = svc._internals;
const arch = { primary: { name: 'BOMBER' } };
it('renders FORM as "—" with no bar when no real form value exists', () => {
const form = buildIntel({}, arch).find((m) => m.label === 'FORM');
expect(form.value).toBe('—');
expect(form.kind).toBe('plain'); // 'plain' draws no progress bar → no width math on a null
expect(form.score).toBeUndefined();
});
it('renders MATCHUP as "—", never a grade letter, with no opponent input', () => {
const m = buildIntel({}, arch).find((x) => x.label === 'MATCHUP');
expect(m.value).toBe('—');
expect(m.kind).toBe('plain');
// and not merely because the archetype was missing — the old code
// hardcoded 'B' on that branch too.
const noArch = buildIntel({}, { primary: null }).find((x) => x.label === 'MATCHUP');
expect(noArch.value).toBe('—');
});
it('the prop count can NEVER move FORM (the fabrication that shipped)', async () => {
const mk = (n) => ({
grades: Array.from({ length: n }, (_, i) => ({
player: 'Josh Bell', stat_type: `stat_${i}`, line: 0.5, direction: 'over', grade: 'B', confidence: 57,
})),
});
const formFor = async (n) => {
const r = await svc.getPlayerIntel('Josh Bell', 'mlb', { cacheGet: cacheWith(mk(n)), stats: {} });
return r.intel.find((m) => m.label === 'FORM').value;
};
expect(await formFor(1)).toBe('—'); // was '74'
expect(await formFor(5)).toBe('—'); // was '90'
expect(await formFor(0)).toBe('—'); // was '70'
});
it('still renders a REAL form value when one is supplied (un-claim, not un-build)', () => {
const form = buildIntel({ form: 63 }, arch).find((m) => m.label === 'FORM');
expect(form.value).toBe('63');
expect(form.kind).toBe('form');
expect(form.score).toBe('63%');
});
it('usage and rest keep their honest absent states', () => {
const rows = buildIntel({}, arch);
expect(rows.find((m) => m.label === 'USAGE').value).toBe('—');
expect(rows.find((m) => m.label === 'REST').value).toBe('—');
expect(buildIntel({ usage: '3.6 AB/G', rest: 'B2B' }, arch).find((m) => m.label === 'USAGE').value).toBe('3.6 AB/G');
});
});
+19 -7
View File
@@ -25,9 +25,17 @@ describe('/u routing — the share surface stays public', () => {
describe('/u/[handle] server shell', () => {
const src = read('app/u/[handle]/page.tsx');
it('carries the CLV-verified metadata title', () => {
expect(src).toContain('CLV-verified record — @');
expect(src).toContain('· VYNDR');
// Session 65 — UN-CLAIM. The metadata claimed a CLV verification that never
// renders on this surface (beat_close_pct is null until C4 is fixed). Copy
// must describe exactly what the page shows and imply no closing-line value.
it('carries honest settled-record metadata and claims no CLV', () => {
expect(src).toContain('Settled record — @');
expect(src).toContain('wins and misses');
expect(src).not.toMatch(/CLV-verified|closing-line value included/);
});
it('does NOT repeat the layout title template suffix (avoids "· VYNDR · VYNDR")', () => {
// layout.tsx metadata template is '%s · VYNDR' — the page title must not add its own.
expect(src).not.toMatch(/const title = `[^`]*· VYNDR`/);
});
it('renders the client record component', () => {
expect(src).toContain('PublicProfile');
@@ -41,11 +49,13 @@ describe('/u/[handle] client record', () => {
expect(src).toContain('RECORD BUILDING');
expect(src).toMatch(/settled >= minSample/);
});
it('shows outcome + CLV chips on settled rows (ledger card patterns)', () => {
it('shows outcome chips on settled rows, and NO CLV claim (Session 65 un-claim)', () => {
expect(src).toContain('✓ HIT');
expect(src).toContain('✕ MISS');
expect(src).toContain('BEAT CLOSE');
expect(src).toContain('clv_result');
// The BEAT CLOSE figure and the ✓ CLV-VERIFIED badge are removed from this
// public surface — both derived from the legacy overwritable closing_line.
expect(src).not.toContain('BEAT CLOSE');
expect(src).not.toContain('CLV-VERIFIED');
});
it('states the uncurated contract and one not-found state for unknown AND unpublished', () => {
expect(src).toContain('Nothing curated');
@@ -63,7 +73,9 @@ describe('/u/[handle] OG image', () => {
expect(og).toContain('width: 1200, height: 630');
expect(og).toContain("contentType = 'image/png'");
expect(og).toContain('PUBLIC LEDGER');
expect(og).toContain('misses included');
// Session 65 — the no-record fallback tagline, un-claimed (was
// "CLV-verified record · every settled read · misses included").
expect(og).toContain('Every settled read · wins and misses · nothing curated');
});
it('does NOT use the edge runtime (self-hosted standalone, S53 rule)', () => {
expect(og).not.toContain("runtime = 'edge'");