checkpoint: chain shadow, WNBA possession feed, baseball chain

Backup commit of uncommitted working-tree state found during Legion
recon (Tony resurrection, STEP 0). This work existed only on the
laptop disk.

- chain shadow accrual + probe script (038_chain_shadow.sql)
- WNBA possession feed: ESPN adapter, usage service, verify script
  (039_wnba_player_game.sql)
- baseball chain
- retention/snapshot service updates, tableKeys, matchupKeys
- specs: chain-v1, wnba-possession-feed, wnba-source-survey
- unit tests for the above

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QnvJAkC3h5QGmb6dipoiWn
This commit is contained in:
Kev
2026-08-14 16:53:37 -04:00
parent 0657b71d18
commit 6c34af3414
22 changed files with 4958 additions and 32 deletions
+19 -3
View File
@@ -76,8 +76,12 @@ async function build(deps = {}) {
let games = [];
try {
[lineups, games] = await Promise.all([
// `batting_order` rides along on a read that already happens — it is the
// OPPORTUNITY term the chain needs (PA per game is set almost entirely by
// lineup slot), and fetching it separately would be a second query for a
// column already in the row. Nothing on the served path reads it.
paginate(() => sb.from('lineup_context')
.select('as_of_date, game_date, sport, game_pk, team, side, player_key')
.select('as_of_date, game_date, sport, game_pk, team, side, player_key, batting_order')
.eq('sport', 'mlb').eq('game_date', gameDate).lte('as_of_date', asOf),
{ key: uniqueKeyFor('lineup_context'), pageSize: 1000, label: 'matchupKeys:lineup_context' }),
getSchedule(gameDate),
@@ -122,15 +126,27 @@ async function build(deps = {}) {
*/
function resolve(prop) {
const key = nameKey(prop && (prop.player || prop.player_name));
const empty = { opponent: null, opposing_pitcher: null, source: null, refused: 'no_lineup_row' };
const empty = { opponent: null, opposing_pitcher: null, batting_order: null, source: null, refused: 'no_lineup_row' };
if (!key) return { ...empty, refused: 'no_player' };
const lu = teamByPlayer.get(key);
if (!lu || !lu.team) return empty;
// The posted batting slot, carried whether or not the matchup resolves — a
// hitter with no probable pitcher declared still has a lineup position, and
// that is the opportunity term. Absent stays absent (never slot 4 by
// default, which would assert he bats cleanup).
const slot = lu.batting_order == null ? null : Number(lu.batting_order);
const battingOrder = Number.isFinite(slot) ? slot : null;
const m = matchup.get(lu.team) || matchup.get(String(lu.team).split(' ').pop());
if (!m) return { opponent: null, opposing_pitcher: null, source: null, refused: 'team_not_in_schedule' };
if (!m) {
return {
opponent: null, opposing_pitcher: null, batting_order: battingOrder,
team: lu.team, source: null, refused: 'team_not_in_schedule',
};
}
return {
opponent: m.opponent || null,
opposing_pitcher: m.opposing_pitcher || null,
batting_order: battingOrder,
team: lu.team,
source: 'lineup_context+schedule',
refused: m.opposing_pitcher ? null : 'no_probable_pitcher',