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:
@@ -4,7 +4,7 @@
|
||||
// real feed values; the refresh recaptures the close.
|
||||
|
||||
const { runIntradayRefresh, inSlateHours, __internals } = require('../../src/services/intradayRefreshService');
|
||||
const { signedDelta } = __internals;
|
||||
const { signedDelta, trackHistory, HISTORY_CAP } = __internals;
|
||||
|
||||
const NOW = '2026-07-11T20:00:00.000Z';
|
||||
|
||||
@@ -125,6 +125,67 @@ describe('runIntradayRefresh — directional handling', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// S6 (A1 board) — sparkline fuel: {t, line} history capture on each grade's
|
||||
// movement tracking, persisted inside the snapshot write-back (no new keys).
|
||||
describe('trackHistory — real {t, line} points, seeded, deduped, capped', () => {
|
||||
const LOCK_TS = '2026-07-11T14:00:00.000Z';
|
||||
const g = (extra = {}) => ({ ...GRADE, gradedAt: { line: 0.5, odds: -115, timestamp: LOCK_TS }, ...extra });
|
||||
|
||||
test('first capture seeds the LOCKED line at its own timestamp, then the move', () => {
|
||||
const h = trackHistory(g(), 1.5, NOW);
|
||||
expect(h).toEqual([{ t: LOCK_TS, line: 0.5 }, { t: NOW, line: 1.5 }]);
|
||||
});
|
||||
|
||||
test('a flat line never appends a duplicate point (a point means it MOVED)', () => {
|
||||
const h = trackHistory(g(), 0.5, NOW); // current == locked
|
||||
expect(h).toEqual([{ t: LOCK_TS, line: 0.5 }]);
|
||||
const again = trackHistory(g({ history: h }), 0.5, '2026-07-11T21:00:00.000Z');
|
||||
expect(again).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('an unparseable current line appends nothing (real points only)', () => {
|
||||
expect(trackHistory(g(), null, NOW)).toBeUndefined();
|
||||
const prior = [{ t: LOCK_TS, line: 0.5 }];
|
||||
expect(trackHistory(g({ history: prior }), 'n/a', NOW)).toBe(prior);
|
||||
});
|
||||
|
||||
test('capped at 24 points, newest kept', () => {
|
||||
const prior = Array.from({ length: HISTORY_CAP }, (_, i) => ({ t: `t${i}`, line: i }));
|
||||
const h = trackHistory(g({ history: prior }), 99, NOW);
|
||||
expect(h).toHaveLength(HISTORY_CAP);
|
||||
expect(h[HISTORY_CAP - 1]).toEqual({ t: NOW, line: 99 });
|
||||
expect(h[0]).toEqual({ t: 't1', line: 1 }); // oldest point aged out
|
||||
});
|
||||
});
|
||||
|
||||
describe('runIntradayRefresh — history rides the snapshot write-back', () => {
|
||||
test('a steam move persists history on the grade (locked point + current)', async () => {
|
||||
const { deps, store } = harness({ grades: [GRADE], oddsProps: [prop(1.5)], analyzeResult: null });
|
||||
await runIntradayRefresh('mlb', deps);
|
||||
const snap = store.get('snapshot:mlb:latest');
|
||||
expect(snap.grades[0].history).toEqual([
|
||||
{ t: NOW, line: 0.5 }, // seeded from the lock (gradedAt.timestamp === NOW here)
|
||||
{ t: NOW, line: 1.5 },
|
||||
]);
|
||||
// grades:{sport} carries the same enriched grades.
|
||||
expect(store.get('grades:mlb').grades[0].history).toHaveLength(2);
|
||||
});
|
||||
|
||||
test('a flat run keeps a single seeded point — the sparkline stays hidden (<3)', async () => {
|
||||
const { deps, store } = harness({ grades: [GRADE], oddsProps: [prop(0.5)], analyzeResult: null });
|
||||
await runIntradayRefresh('mlb', deps);
|
||||
expect(store.get('snapshot:mlb:latest').grades[0].history).toEqual([{ t: NOW, line: 0.5 }]);
|
||||
});
|
||||
|
||||
test('a prop gone from the feed leaves prior history untouched', async () => {
|
||||
const withHist = { ...GRADE, stat_type: 'hits', history: [{ t: 'x', line: 1 }] };
|
||||
const { deps, store } = harness({ grades: [withHist], oddsProps: [prop(1.5)], analyzeResult: null });
|
||||
await runIntradayRefresh('mlb', deps);
|
||||
const frozen = store.get('snapshot:mlb:latest').grades.find((g) => g.stat_type === 'hits');
|
||||
expect(frozen.history).toEqual([{ t: 'x', line: 1 }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inSlateHours — noon–midnight ET', () => {
|
||||
test('3 PM ET yes, 4 AM ET no', () => {
|
||||
expect(inSlateHours(new Date('2026-07-11T19:00:00Z'))).toBe(true); // 3 PM EDT
|
||||
|
||||
Reference in New Issue
Block a user