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:
@@ -211,30 +211,46 @@ function mlbProfileIntel(res) {
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Derive the VYNDR Intelligence metric row from whatever we have. */
|
||||
function buildIntel(stats, arch, propCount) {
|
||||
/**
|
||||
* Derive the VYNDR Intelligence metric row from whatever we have.
|
||||
*
|
||||
* Session 65 — UN-FABRICATE (Truth Law: if it renders a number, it comes from
|
||||
* real data or it does not render).
|
||||
* - FORM used to fall back to `70 + 4 × (count of tonight's graded props)`,
|
||||
* capped at 92. Nothing on the HTTP path ever sets `stats.form`, so that
|
||||
* fallback WAS the live value: Josh Bell's "74" was 70 + 4×1 graded prop —
|
||||
* a number carrying zero information about the player. Absent now renders
|
||||
* absent.
|
||||
* - MATCHUP used to be `gradeFromForm(form)` (i.e. the same prop count) with a
|
||||
* hardcoded 'B' when no archetype resolved. There is no opponent input on
|
||||
* this path at all, so BOTH branches were fabricated letters. Absent now
|
||||
* renders absent.
|
||||
* A real form metric is a separate, held design call (per-sport headline stat);
|
||||
* it lands as `stats.form` / `stats.matchup` and lights these tiles back up.
|
||||
* Tiles emit kind 'plain' when absent so the page prints "—" and draws no
|
||||
* progress bar — no math, colour or bar width is computed off a null.
|
||||
*/
|
||||
function buildIntel(stats, arch) {
|
||||
const clamp = (n, lo, hi) => Math.max(lo, Math.min(hi, n));
|
||||
// Form: lean on last-10 vs season if provided, else a neutral baseline that
|
||||
// scales gently with the strongest graded prop's confidence proxy.
|
||||
const form = stats.form != null ? clamp(Math.round(stats.form), 0, 100) : 70 + clamp(propCount * 4, 0, 22);
|
||||
const form = stats.form != null && Number.isFinite(Number(stats.form))
|
||||
? clamp(Math.round(Number(stats.form)), 0, 100)
|
||||
: null;
|
||||
// Session 48 — prefer a preformatted usage string (e.g. "3.6 AB/G"); rest
|
||||
// shows the real value or "—" (no more bogus "+0%").
|
||||
const usage = stats.usage || (stats.usg != null ? `${stats.usg}%` : stats.k9 != null ? `${stats.k9} K/9` : '—');
|
||||
const matchup = typeof stats.matchup === 'string' && stats.matchup ? stats.matchup : null;
|
||||
return [
|
||||
{ label: 'FORM', kind: 'form', value: String(form), score: `${clamp(form, 0, 100)}%`, color: form >= 85 ? '#00ffb8' : form >= 70 ? '#00D4A0' : '#FFB347' },
|
||||
form != null
|
||||
? { label: 'FORM', kind: 'form', value: String(form), score: `${form}%`, color: form >= 85 ? '#00ffb8' : form >= 70 ? '#00D4A0' : '#FFB347' }
|
||||
: { label: 'FORM', kind: 'plain', value: '—', color: '#7A7A8E' },
|
||||
{ label: 'USAGE', kind: 'plain', value: usage, color: '#e8e8f0' },
|
||||
{ label: 'MATCHUP', kind: 'grade', value: arch.primary ? gradeFromForm(form) : 'B', color: '#00D4A0' },
|
||||
{ label: 'REST', kind: 'plain', value: stats.rest || '—', color: '#00D4A0' },
|
||||
matchup
|
||||
? { label: 'MATCHUP', kind: 'grade', value: matchup, color: '#00D4A0' }
|
||||
: { label: 'MATCHUP', kind: 'plain', value: '—', color: '#7A7A8E' },
|
||||
{ label: 'REST', kind: 'plain', value: stats.rest || '—', color: stats.rest ? '#00D4A0' : '#7A7A8E' },
|
||||
];
|
||||
}
|
||||
|
||||
function gradeFromForm(form) {
|
||||
if (form >= 90) return 'A';
|
||||
if (form >= 80) return 'B+';
|
||||
if (form >= 70) return 'B';
|
||||
return 'C';
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the full player intelligence payload.
|
||||
* opts: { cacheGet, stats, season, last10, splits, gradeHistory, injury, team }
|
||||
@@ -289,7 +305,9 @@ async function getPlayerIntel(name, sport, opts = {}) {
|
||||
splits: resolved.splits || opts.splits || [],
|
||||
gradeHistory: opts.gradeHistory || [],
|
||||
activeProps,
|
||||
intel: buildIntel(realStats, archetype, props.length),
|
||||
// Session 65 — prop count is deliberately NOT passed any more: it used to
|
||||
// manufacture the FORM number. Intel is a function of real stats only.
|
||||
intel: buildIntel(realStats, archetype),
|
||||
injury: opts.injury || null,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user