S6 (a1): display — the full picture under the grammar
- specs/ROW-GRAMMAR.md: the row grammar law (slot order, color law, mark
law, mobile stacking, no-truncation), locked by tests/unit/rowGrammar.
StatStrip violations fixed: MovementChip before the grade (market
context before model output); ViabilityChips after the archetype
(identity is one contiguous run).
- Line-movement sparklines: intradayRefreshService.trackHistory captures
real {t,line} points per grade (seeded with the lock, deduped when
flat, capped 24) inside the snapshot write-back; StatStrip.LineSparkline
renders at >=3 points (green toward / amber against / dim flat).
- Last-10 dot strips: services/last10Dots (streaksService accessors) ->
/api/snapshot/:sport attaches last10_dots from rosterlogs:{sport};
StatStrip.DotStrip renders vs the LOCKED line, newest first.
- CLV distribution: getModelAggregate emits clv_distribution (7 signed
buckets, outliers clamped) only past the centralized n>=20 gate;
ledger MODEL header renders the bar strip.
- Global search: SearchModal (cmd-K via GlobalHosts + window.__search),
players via /api/players/search per sport + static lib/teams.js
(soccer deliberately absent); Nav search icon + Search first in the
mobile More sheet. Explore tab untouched.
- Landing LCP: fade-up floored at opacity .6 (hero h1 contentful on
first frame, S33 visible-floor rule) + IBM Plex Mono preload:false
(4 decorative font files off the slow-4G critical path).
2654 -> 2698 tests (226 suites) green; web build exit 0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,9 @@ const express = require('express');
|
||||
const { createRateLimit } = require('../middleware/rateLimit');
|
||||
const { cacheGet } = require('../utils/redis');
|
||||
const { nameKey } = require('../utils/playerName');
|
||||
// S6 (A1 board) — ●●○●● last-10 vs tonight's locked line, computed from the
|
||||
// rosterlogs blob the snapshot pipeline already writes. Pure, cache-only.
|
||||
const { indexRosterLogs, attachLast10Dots } = require('../services/last10Dots');
|
||||
|
||||
const router = express.Router();
|
||||
router.use(createRateLimit({ windowMs: 60_000, max: 60 }));
|
||||
@@ -79,20 +82,23 @@ router.get('/summary', async (req, res) => {
|
||||
router.get('/:sport', async (req, res) => {
|
||||
const sport = String(req.params.sport || '').toLowerCase();
|
||||
try {
|
||||
const [snap, outcomeLog] = await Promise.all([
|
||||
const [snap, outcomeLog, rosterBlob] = await Promise.all([
|
||||
cacheGet(`snapshot:${sport}:latest`),
|
||||
cacheGet(`outcomes:${sport}:log`),
|
||||
cacheGet(`rosterlogs:${sport}`),
|
||||
]);
|
||||
const idx = outcomeIndex(outcomeLog);
|
||||
const roster = indexRosterLogs(rosterBlob);
|
||||
const enrich = (grades) => attachLast10Dots(attachOutcomes(grades, idx), roster, sport);
|
||||
if (snap && Array.isArray(snap.grades)) {
|
||||
res.set('Cache-Control', 'public, max-age=30');
|
||||
return res.json({ sport, updated_at: snap.updated_at, grades: attachOutcomes(snap.grades, idx), deltas: snap.deltas || [] });
|
||||
return res.json({ sport, updated_at: snap.updated_at, grades: enrich(snap.grades), deltas: snap.deltas || [] });
|
||||
}
|
||||
// Fallback: the grades envelope (no deltas yet).
|
||||
const env = await cacheGet(`grades:${sport}`);
|
||||
const grades = env && Array.isArray(env.grades) ? env.grades : [];
|
||||
res.set('Cache-Control', 'public, max-age=30');
|
||||
return res.json({ sport, updated_at: env && env.updated_at, grades: attachOutcomes(grades, idx), deltas: [] });
|
||||
return res.json({ sport, updated_at: env && env.updated_at, grades: enrich(grades), deltas: [] });
|
||||
} catch (err) {
|
||||
console.error('[snapshot]', err.message);
|
||||
return res.status(200).json({ sport, grades: [], deltas: [] });
|
||||
|
||||
Reference in New Issue
Block a user