Audit endpoint: where the env/matchup context chain drops off
The arch-v1 environment and matchup axes fired on ZERO prod rows across 634 graded props while archetype axes fired normally, and buildContext works locally (15 games, 14 with weather, Coors composing to 1.241). So the failure is downstream of buildContext and has to be located, not inferred from an absence. Replays buildContext + contextFor against the CURRENT cached snapshot grades and counts the drop-off at each hop: team field present -> resolves to an abbr -> abbr matches a game -> environment produced; and bats / playerId / opposing-pitcher known -> matchup produced. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QJs13VsyiSKYQP6rj3NNmc
This commit is contained in:
@@ -652,4 +652,73 @@ router.get('/feature-coverage', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* GET /api/internal/env-context-audit (matchup/park audit, Step 0)
|
||||
*
|
||||
* READ-ONLY. The arch-v1 environment and matchup axes fired on ZERO prod rows
|
||||
* while archetype axes fired normally, so the question is WHERE the resolution
|
||||
* chain drops off. This replays buildContext + contextFor against the CURRENT
|
||||
* cached snapshot grades and counts the drop-off at each hop, instead of
|
||||
* inferring it from an absence.
|
||||
*/
|
||||
router.get('/env-context-audit', async (req, res) => {
|
||||
try {
|
||||
const sport = String(req.query.sport || 'mlb').toLowerCase();
|
||||
const { cacheGet } = require('../utils/redis');
|
||||
const envCtx = require('../services/environmentContext');
|
||||
|
||||
const snap = await cacheGet(`snapshot:${sport}:latest`);
|
||||
const grades = (snap && Array.isArray(snap.grades) ? snap.grades : []).slice(0, 120);
|
||||
|
||||
const ctx = await envCtx.buildContext(sport, {
|
||||
origin: process.env.BACKEND_SELF_ORIGIN || 'http://localhost:3000',
|
||||
});
|
||||
const i = ctx._internals || {};
|
||||
|
||||
const counts = {
|
||||
grades_examined: grades.length,
|
||||
with_team_field: 0,
|
||||
team_resolves_to_abbr: 0,
|
||||
abbr_matches_a_game: 0,
|
||||
got_environment: 0,
|
||||
with_bats: 0,
|
||||
with_playerId: 0,
|
||||
opp_pitcher_known: 0,
|
||||
got_matchup: 0,
|
||||
};
|
||||
const sampleTeams = [];
|
||||
for (const g of grades) {
|
||||
const team = g.team;
|
||||
if (team) counts.with_team_field += 1;
|
||||
const abbr = envCtx.abbrOf(team);
|
||||
if (abbr) counts.team_resolves_to_abbr += 1;
|
||||
if (sampleTeams.length < 8 && team) sampleTeams.push({ team, abbr: abbr || null });
|
||||
if (abbr && i.gameByTeam && i.gameByTeam.has(abbr)) counts.abbr_matches_a_game += 1;
|
||||
if (g.bats) counts.with_bats += 1;
|
||||
if (g.playerId != null) counts.with_playerId += 1;
|
||||
if (abbr && i.oppPitcherByTeam && i.oppPitcherByTeam.get(abbr) != null) counts.opp_pitcher_known += 1;
|
||||
const r = await ctx.contextFor(g);
|
||||
if (r && r.environment) counts.got_environment += 1;
|
||||
if (r && r.matchup) counts.got_matchup += 1;
|
||||
}
|
||||
|
||||
res.set('Cache-Control', 'no-store');
|
||||
return res.json({
|
||||
ok: true,
|
||||
read_only: true,
|
||||
build_context_stats: ctx.stats,
|
||||
internals_sizes: {
|
||||
gameByTeam: i.gameByTeam ? i.gameByTeam.size : null,
|
||||
forecastByHome: i.forecastByHome ? i.forecastByHome.size : null,
|
||||
oppPitcherByTeam: i.oppPitcherByTeam ? i.oppPitcherByTeam.size : null,
|
||||
handById: i.handById ? i.handById.size : null,
|
||||
},
|
||||
drop_off: counts,
|
||||
sample_team_values: sampleTeams,
|
||||
});
|
||||
} catch (err) {
|
||||
return res.status(500).json({ ok: false, error: err && err.message, stack_head: String(err && err.stack || '').split('\n')[1] || null });
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user