Render directional-CLV badge — Analyst+Desk, server-gated, receipt-bearing
CARD BADGE ONLY. Ticker-CLV explicitly DEFERRED (named, not lost).
PHASE 1 — SERVER-SIDE GATE AT THE DATA LAYER. A Free request never
RECEIVES dclv data: the CLV columns are appended to the SELECT only behind
canAccess(tier,'clv_badge') (new capability, analyst+desk), and responses
are ALSO stripped as defence in depth so a future SELECT change cannot
quietly leak. No CSS/client gate — data that reaches the browser has left
the building. dclv_fair_lock/fair_close are de-vig internals and are never
sent at all.
SURFACE AUDIT, all six channels, each test-locked to contain no CLV:
public profile (share link), snapshot/card feed, ticker feed, share
card/OG, embeddable widget, newsletter. A test also asserts no
ledger_entries read uses select('*') — a star would auto-leak every new
column, which is exactly how a gate becomes theatre.
PHASE 2 — IMMUTABLE ONCE COMPUTED. A settle can re-run (stat correction,
protested game) and a badge that flips positive->negative AFTER a user saw
or screenshotted it is a credibility failure. First computation wins: dclv
is only computed when dclv_computed_at is null, so a re-settle can never
rewrite a shown badge. Same discipline as the locked grade.
PHASE 3 — RENDER, test-first, ABSENCE IS HONEST. unknown / flat / null /
missing-receipt all render NOTHING — no element, no placeholder, no
"pending". Proven on an ALL-NULL board (today: 0 badges) and a MIXED board
(tomorrow: 1 of 4 badged, badge-less cards clean). Binary states only:
positive -> MOVED TOWARD US "graded -110 · closed -145"
negative -> MOVED AWAY "graded -110 · closed +120"
The RECEIPT is the persuasive part, so a badge with no numbers is
suppressed rather than shown as a bare claim. Negative is neutral context
and NEVER touches the locked grade — no back-door re-grading.
NO aggregate, count or rollup exists by construction: the module exports
exactly {clvBadge, fmtPrice} and a badge payload carries exactly
{tone,label,receipt} — asserted by test, because an on-screen tally would
be the held aggregate claim through the side door.
Build gotcha hit and fixed: clvBadge is CommonJS (allowJs) with no TS
types, so the .tsx needed an explicit cast at the call site — the build
worker exits 1 on type errors even though compilation "succeeds".
Suite 288/3473 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:
+8
-2
@@ -74,7 +74,10 @@ const TIERS = Object.freeze({
|
||||
// Session 62 (A1-S1) — Desk-only intelligence (pricing-page promises).
|
||||
alt_line_ladder: false,
|
||||
kelly_sizing: false,
|
||||
}),
|
||||
// Session 64 — per-read directional-CLV badge. Analyst+Desk only;
|
||||
// Free never RECEIVES the data (gated server-side, not hidden client-side).
|
||||
clv_badge: true,
|
||||
}),
|
||||
desk: Object.freeze({
|
||||
scans_per_day: Infinity,
|
||||
grade_visible: true,
|
||||
@@ -88,7 +91,10 @@ const TIERS = Object.freeze({
|
||||
api_access: false,
|
||||
alt_line_ladder: true,
|
||||
kelly_sizing: true,
|
||||
}),
|
||||
// Session 64 — per-read directional-CLV badge. Analyst+Desk only;
|
||||
// Free never RECEIVES the data (gated server-side, not hidden client-side).
|
||||
clv_badge: true,
|
||||
}),
|
||||
});
|
||||
|
||||
const VALID_TIERS = Object.freeze(Object.keys(TIERS));
|
||||
|
||||
Reference in New Issue
Block a user