Remove legacy C4 CLV from FIVE surfaces; install directional badge on ledger

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SmNjJAwEnqHPtXbvSZR8kA
This commit is contained in:
Kev
2026-07-20 13:58:47 -04:00
parent da8bfdf1db
commit ca9ca34cbb
5 changed files with 114 additions and 28 deletions
+5 -1
View File
@@ -505,7 +505,11 @@ export default function DashboardPage() {
</p>
<p className="mono" style={{ fontSize: 12, fontWeight: 800, color: 'var(--grade-a)', marginTop: 8, fontVariantNumeric: 'tabular-nums' }}>
&#10003; HIT{r.actual != null ? ` (${r.actual})` : ''}
{r.clvResult === 'beat' ? <span style={{ color: 'var(--text-tertiary)', fontWeight: 700 }}> · CLV BEAT</span> : 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. */}
</p>
</div>
))}
+7 -13
View File
@@ -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 (
<span className="mono" title={`Closing line value: locked ${row.line}, closed ${row.closing_line}`}
style={{ fontSize: 10.5, fontWeight: 700, color, letterSpacing: '0.04em' }}>
CLV {row.clv > 0 ? '+' : ''}{row.clv} · {row.clv_result.toUpperCase()}
</span>
);
}
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
</p>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
<OutcomeChip row={row} />
<ClvChip row={row} />
{/* 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. */}
<ClvBadge read={row as unknown as Record<string, unknown>} />
</div>
</article>
);
+6 -12
View File
@@ -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 (
<span className="mono" title={`Closing line value: locked ${row.line}, closed ${row.closing_line}`}
style={{ fontSize: 10.5, fontWeight: 700, color, letterSpacing: '0.04em' }}>
CLV {row.clv > 0 ? '+' : ''}{row.clv} · {row.clv_result.toUpperCase()}
</span>
);
}
// 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 }) {
</p>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
<OutcomeChip row={row} />
<ClvChip row={row} />
</div>
</article>
);
+6 -2
View File
@@ -419,7 +419,9 @@ function YesterdaySettle({ date }: { date: string }) {
<span style={{ fontWeight: 800, color: r.outcome === 'hit' ? 'var(--g-a)' : r.outcome === 'miss' ? 'var(--miss)' : 'var(--text-1)' }}>
{r.outcome === 'hit' ? '✓ HIT' : r.outcome === 'miss' ? '✕ MISS' : ' PUSH'}{r.actual_value != null ? ` (${r.actual_value})` : ''}
</span>
{r.clv_result && <span style={{ fontSize: 10.5, color: r.clv_result === 'beat' ? 'var(--g-a)' : r.clv_result === 'faded' ? 'var(--miss)' : 'var(--text-2)' }}>CLV {r.clv_result.toUpperCase()}</span>}
{/* 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. */}
</div>
))}
</div>
@@ -512,7 +514,9 @@ function OutlookSurface({ tab }: { tab: SlateTab }) {
{String(r.side).toUpperCase().startsWith('U') ? 'u' : 'o'}{r.line} {String(r.stat).replace(/_/g, ' ')}
</div>
<div style={{ fontSize: 11.5, fontWeight: 800, color: 'var(--g-a)', marginTop: 6 }}>
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})` : ''}
</div>
</div>
))}