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:
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user