diff --git a/tests/unit/clvLegacyRemoval.test.js b/tests/unit/clvLegacyRemoval.test.js new file mode 100644 index 0000000..0ba2236 --- /dev/null +++ b/tests/unit/clvLegacyRemoval.test.js @@ -0,0 +1,90 @@ +/** + * Session 64 — the legacy C4 CLV display is GONE from every surface. + * + * TRUTH FIX, not a placement. row.clv/clv_result derive from the overwritable + * closing_line: 637/699 rows had closing == locked, so 522 of the 578 rendered + * "flat"s encoded a CAPTURE FAILURE as a held line. That number was live on + * FIVE surfaces, two of them public. + * + * These lock the removal so it cannot quietly return, and lock the distinction + * between the two CLV lineages. + */ +const fs = require('fs'); +const path = require('path'); +const read = (f) => fs.readFileSync(path.join(__dirname, '..', '..', f), 'utf8'); + +const SURFACES = { + 'ledger card (authed)': 'web/src/app/ledger/page.tsx', + 'public profile (share link)': 'web/src/app/u/[handle]/PublicProfile.tsx', + 'dashboard': 'web/src/app/dashboard/page.tsx', + 'board / Slate': 'web/src/components/Slate.tsx', +}; + +// Comments explaining the removal legitimately mention the old names. +const stripComments = (src) => src + .replace(/\/\*[\s\S]*?\*\//g, '') + .split('\n').filter((l) => !l.trim().startsWith('//')).join('\n'); + +describe('LEGACY C4 CLV — removed from every surface', () => { + for (const [name, file] of Object.entries(SURFACES)) { + test(`${name} renders no legacy CLV`, () => { + const code = stripComments(read(file)); + expect(code).not.toMatch(/clv_result\.toUpperCase/); + expect(code).not.toMatch(/CLV \{row\.clv/); + expect(code).not.toMatch(/CLV BEAT/); + }); + } + + test('the ClvChip component itself no longer exists', () => { + expect(stripComments(read(SURFACES['ledger card (authed)']))).not.toMatch(/function ClvChip/); + expect(stripComments(read(SURFACES['public profile (share link)']))).not.toMatch(/ClvChip/); + }); +}); + +describe('THE TWO LINEAGES MUST NEVER CROSS', () => { + test('the new badge reads ONLY dclv_state, never row.clv/clv_result', () => { + const badge = read('web/src/lib/clvBadge.js'); + expect(badge).toMatch(/dclv_state/); + expect(badge).not.toMatch(/clv_result/); + expect(badge).not.toMatch(/read\.clv\b/); + }); + + test('the ledger row renders the directional badge in the old slot', () => { + const src = read(SURFACES['ledger card (authed)']); + expect(src).toMatch(/ { + expect(read(SURFACES['public profile (share link)'])).not.toMatch(/ClvBadge/); + }); +}); + +describe('HONEST ABSENCE — not a subtler "flat"', () => { + const { clvBadge } = require('../../web/src/lib/clvBadge'); + + test('a formerly-chipped historical row now renders NOTHING', () => { + // Exactly the shape of the 578 rows that used to show "CLV 0 · FLAT". + const legacyRow = { clv: 0, clv_result: 'flat', locked_odds: '-110', closing_line: 1.5, outcome: 'hit' }; + expect(clvBadge(legacyRow)).toBeNull(); + }); + + test('rows that will NEVER get dclv (settled pre-capture) stay silent forever', () => { + expect(clvBadge({ outcome: 'miss', locked_odds: '-120', dclv_state: null })).toBeNull(); + }); + + test('absence makes NO claim — there is no "flat"/"no movement" output path', () => { + const states = ['unknown', 'flat', null, undefined]; + for (const st of states) { + expect(clvBadge({ dclv_state: st, locked_odds: '-110', closing_odds: '-145' })).toBeNull(); + } + }); +}); + +describe('NO CLV SORT/FILTER (a soft aggregate)', () => { + test('the ledger exposes no CLV sort or filter control', () => { + const code = stripComments(read(SURFACES['ledger card (authed)'])); + expect(code).not.toMatch(/sort.*dclv|dclv.*sort/i); + expect(code).not.toMatch(/filter.*dclv|dclv.*filter/i); + }); +}); diff --git a/web/src/app/dashboard/page.tsx b/web/src/app/dashboard/page.tsx index d9682da..12e2287 100644 --- a/web/src/app/dashboard/page.tsx +++ b/web/src/app/dashboard/page.tsx @@ -505,7 +505,11 @@ export default function DashboardPage() {

✓ HIT{r.actual != null ? ` (${r.actual})` : ''} - {r.clvResult === 'beat' ? · CLV BEAT : null} + {/* Session 64 — legacy C4 CLV removed. It read row.clvResult, + derived from the overwritable closing_line (637/699 closing == + locked), so "BEAT" was unreliable and "flat" mostly meant the + close was never captured. The verified directional badge is + Analyst+Desk on the ledger card; nothing is shown here. */}

))} diff --git a/web/src/app/ledger/page.tsx b/web/src/app/ledger/page.tsx index b57b3b9..c818377 100644 --- a/web/src/app/ledger/page.tsx +++ b/web/src/app/ledger/page.tsx @@ -8,6 +8,7 @@ import { gradeColor } from '@/lib/vyndrTokens'; import { useAuth } from '@/contexts/AuthContext'; import { Skeleton, EmptyState, ArchetypeBadge, BookWordmark, TierRecord } from '@/components/vyndr'; import { clvMode, CLV_FLAT_LINE } from '@/lib/clvDisplay'; +import ClvBadge from '@/components/vyndr/ClvBadge'; /** * The Ledger (Session 58, Phase 1) — the truth surface, now backed by the @@ -316,18 +317,6 @@ function OutcomeChip({ row }: { row: LedgerRow }) { ); } -function ClvChip({ row }: { row: LedgerRow }) { - if (!row.clv_result || row.clv == null) return null; - const color = row.clv_result === 'beat' ? 'var(--g-a, #00D4A0)' - : row.clv_result === 'faded' ? 'var(--miss, #FF6B6B)' : 'var(--text-tertiary)'; - return ( - - CLV {row.clv > 0 ? '+' : ''}{row.clv} · {row.clv_result.toUpperCase()} - - ); -} - function LedgerCard({ row, index, ladder }: { row: LedgerRow; index: number; ladder?: LedgerRow[] }) { const sportColor = SPORT_COLOR[row.sport] || 'var(--text-secondary)'; const rungs = Array.isArray(ladder) ? ladder : [row]; @@ -386,7 +375,12 @@ function LedgerCard({ row, index, ladder }: { row: LedgerRow; index: number; lad

- + {/* Session 64 — the RESOLVED-AT-CLOSE chapter. Three time states on this + row read distinctly: entry (locked_odds, at-grade) · close (this badge, + at-close) · outcome (hit/miss, final). The RESULT stays the hero; this + is supporting context. Absent dclv renders NOTHING — not a "flat", + which is the legacy chip's lie in subtler form. */} + } />
); diff --git a/web/src/app/u/[handle]/PublicProfile.tsx b/web/src/app/u/[handle]/PublicProfile.tsx index 8c9e3a9..82897fc 100644 --- a/web/src/app/u/[handle]/PublicProfile.tsx +++ b/web/src/app/u/[handle]/PublicProfile.tsx @@ -256,17 +256,12 @@ function OutcomeChip({ row }: { row: ProfileRow }) { ); } -function ClvChip({ row }: { row: ProfileRow }) { - if (!row.clv_result || row.clv == null) return null; - const color = row.clv_result === 'beat' ? 'var(--g-a, #00D4A0)' - : row.clv_result === 'faded' ? 'var(--miss, #FF6B6B)' : 'var(--text-tertiary)'; - return ( - - CLV {row.clv > 0 ? '+' : ''}{row.clv} · {row.clv_result.toUpperCase()} - - ); -} +// Session 64 — the legacy C4 CLV chip was REMOVED from this PUBLIC surface. +// It rendered row.clv/clv_result, built on the overwritable closing_line where +// 637/699 rows had closing == locked, so 522 of its 578 "flat"s encoded a +// CAPTURE FAILURE as a held line. Public profiles now show the settled result +// only. The directional badge is Analyst+Desk and is deliberately NOT added +// here — this surface is public and never receives dclv data. function ProfileCard({ row, index }: { row: ProfileRow; index: number }) { const sportColor = SPORT_COLOR[row.sport] || 'var(--text-secondary)'; @@ -301,7 +296,6 @@ function ProfileCard({ row, index }: { row: ProfileRow; index: number }) {

-
); diff --git a/web/src/components/Slate.tsx b/web/src/components/Slate.tsx index 4930107..833fb91 100644 --- a/web/src/components/Slate.tsx +++ b/web/src/components/Slate.tsx @@ -419,7 +419,9 @@ function YesterdaySettle({ date }: { date: string }) { {r.outcome === 'hit' ? '✓ HIT' : r.outcome === 'miss' ? '✕ MISS' : '– PUSH'}{r.actual_value != null ? ` (${r.actual_value})` : ''} - {r.clv_result && CLV {r.clv_result.toUpperCase()}} + {/* Session 64 — legacy C4 CLV removed from the board. Same broken + lineage as the ledger chip; the board is also a FREE surface and the + verified badge is Analyst+Desk. */} ))} @@ -512,7 +514,9 @@ function OutlookSurface({ tab }: { tab: SlateTab }) { {String(r.side).toUpperCase().startsWith('U') ? 'u' : 'o'}{r.line} {String(r.stat).replace(/_/g, ' ')}
- ✓ HIT{r.actual != null ? ` (${r.actual})` : ''}{r.clvResult === 'beat' ? ' · CLV BEAT' : ''} + {/* Session 64 — legacy C4 "· CLV BEAT" removed here too; the + result stands on its own. */} + ✓ HIT{r.actual != null ? ` (${r.actual})` : ''}
))}