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:
Kev
2026-06-19 02:42:13 -04:00
parent 78db55d499
commit 91b03c4044
10 changed files with 259 additions and 15 deletions
+12 -1
View File
@@ -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) ──────────────────────────────