Session 48: Name normalization at every layer + usage field (2156 tests)
Trace-first: the normalizer functions were correct (S47) but raw names still
flowed through paths that skipped them. Fixed each leaking path.
- 2a (source chokepoint): snapshotService.runSnapshot normalizes each grade's
player to the de-dotted display AND dedupes to one grade per nameKey|stat
(highest confidence) before writing grades:{sport} + snapshot:latest. Every
consumer (GameCard, Explore, leaders, profile) now gets clean merged names.
- 2b: buildPlayerStripsFromProps dedupes a player's props by stat (graded >
awaiting) → one row per stat (kills "Ks 5.5 AND Ks 3.5" variant dupes).
- 2c: scan tonightsPlayers grid groups by nameKey, displays normalized name.
- 3: profile VYNDR INTELLIGENCE "+0%"/"—" was buildIntel's defaults (separate
from the grade card's buildIntelFields, which already works). resolvePlayerStats
now attaches real usage (AB/G) + rest (B2B/Xd); buildIntel renders them; REST
default is now "—".
Backend 2149 -> 2156 tests (+7), 181 suites. Web build clean (exit 0).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
File diff suppressed because one or more lines are too long
@@ -5,6 +5,7 @@ import { useRouter } from 'next/navigation';
|
||||
import ProcessingGrade from '@/components/vyndr/ProcessingGrade';
|
||||
import type { GradeResultData } from '@/components/vyndr/GradeResultCard';
|
||||
import { mapScanToGradeResult } from '@/lib/gradeAdapter';
|
||||
import { normalizeName, nameKey } from '@/lib/playerName';
|
||||
import { markReadComplete } from '@/lib/reads';
|
||||
import { useAuth } from '@/contexts/AuthContext';
|
||||
import { useParlay } from '@/contexts/ParlayContext';
|
||||
@@ -175,15 +176,21 @@ export default function ScanPage() {
|
||||
})
|
||||
.then((data: { props?: Array<{ player?: string; stat_type?: string }> }) => {
|
||||
if (cancelled) return;
|
||||
const byPlayer = new Map<string, Set<string>>();
|
||||
// Session 48 — group by the normalized name key so variants
|
||||
// ("A.J."/"AJ", "Matt"/"Matthew", "Jazz Chisholm"/"Jr.") show as ONE
|
||||
// tile; display the normalized (longest) name.
|
||||
const byPlayer = new Map<string, { name: string; stats: Set<string> }>();
|
||||
for (const p of data.props || []) {
|
||||
if (!p.player || !p.stat_type) continue;
|
||||
const set = byPlayer.get(p.player) || new Set<string>();
|
||||
set.add(p.stat_type);
|
||||
byPlayer.set(p.player, set);
|
||||
const key = nameKey(p.player);
|
||||
const disp = normalizeName(p.player).display || p.player;
|
||||
const entry = byPlayer.get(key) || { name: disp, stats: new Set<string>() };
|
||||
if (disp.length > entry.name.length) entry.name = disp;
|
||||
entry.stats.add(p.stat_type);
|
||||
byPlayer.set(key, entry);
|
||||
}
|
||||
const list = Array.from(byPlayer.entries())
|
||||
.map(([name, stats]) => ({ name, stats: Array.from(stats) }))
|
||||
const list = Array.from(byPlayer.values())
|
||||
.map(({ name, stats }) => ({ name, stats: Array.from(stats) }))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
setTonightsPlayers(list);
|
||||
})
|
||||
|
||||
@@ -291,7 +291,18 @@ function buildPlayerStripsFromProps(gameProps, gradeIndex, deltaIndex, now = Dat
|
||||
});
|
||||
}
|
||||
}
|
||||
return order.map((key) => byPlayer[key]);
|
||||
// Session 48 — one prop row per stat (variant dupes like "Ks 5.5" + "Ks 3.5"
|
||||
// from "Matt"/"Matthew" collapse). Prefer the graded prop over an awaiting one.
|
||||
return order.map((key) => {
|
||||
const e = byPlayer[key];
|
||||
const byStat = new Map();
|
||||
for (const pr of e.props) {
|
||||
const sk = String(pr.stat).toLowerCase();
|
||||
const ex = byStat.get(sk);
|
||||
if (!ex || (pr.grade && !ex.grade)) byStat.set(sk, pr);
|
||||
}
|
||||
return { ...e, props: [...byStat.values()] };
|
||||
});
|
||||
}
|
||||
|
||||
// ── MLB probable pitchers (Session 46) ──────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user