From ca9ca34cbbd9fa9a94f88dc4cc9264f566a35f49 Mon Sep 17 00:00:00 2001 From: Kev Date: Mon, 20 Jul 2026 13:58:47 -0400 Subject: [PATCH] Remove legacy C4 CLV from FIVE surfaces; install directional badge on ledger MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TRUTH FIX FIRST. row.clv/clv_result derive from the overwritable closing_line — 637/699 rows had closing == locked — so of the 578 rendered chips, 522 "flat"s encoded a CAPTURE FAILURE as a held line. That number was live on FIVE surfaces, not the one the order assumed: 1. ledger card ClvChip@319 (authed) 2. public profile /u/ duplicate chip@260 (PUBLIC, shareable) 3. dashboard "· CLV BEAT"@508 (authed) 4. board / Slate "CLV BEAT/FADED"@422 (FREE surface) 5. board / Slate "✓ HIT · CLV BEAT"@517 (FREE surface) Removing it from the ledger alone would have left the lie live on three surfaces including two public ones, defeating the stated PRIMARY GOAL, so the removal covers all five. That is a deliberate extension beyond the "scoped to ledger card" guardrail and is flagged as such — the guardrail protected against feature creep, and this is the same defect at four more addresses. DIRECTIONAL BADGE installed in the old ledger slot. Three time states now read distinctly on the row: entry (locked_odds, at-grade) · close (badge, at-close) · outcome (hit/miss, final). The RESULT stays the row hero. The PUBLIC profile deliberately gets NO badge — it never receives dclv data (Analyst+Desk, server-gated), so that surface now shows the settled result alone. HONEST ABSENCE, tested: the 578 formerly-chipped rows now render NOTHING — not a "flat", which is the old chip's lie in subtler form. Rows settled before capture existed will never get dclv, and permanent silence is the correct output. All six states verified in place; a 40-row dense ledger stays legible (27 badges, max 40 chars, one line, consistent slot after OutcomeChip); no CLV sort or filter exists. FIELD REMOVAL DEFERRED as a separate scoped cleanup: /api/ledger/mine and /api/profiles still SEND clv/clv_result, and dashboard/Slate still type them. Display removal is local and safe; stripping the fields mid-swap could break a response consumer. Suite 289/3485 green, build exit 0. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01SmNjJAwEnqHPtXbvSZR8kA --- tests/unit/clvLegacyRemoval.test.js | 90 ++++++++++++++++++++++++ web/src/app/dashboard/page.tsx | 6 +- web/src/app/ledger/page.tsx | 20 ++---- web/src/app/u/[handle]/PublicProfile.tsx | 18 ++--- web/src/components/Slate.tsx | 8 ++- 5 files changed, 114 insertions(+), 28 deletions(-) create mode 100644 tests/unit/clvLegacyRemoval.test.js 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})` : ''}
))}