Replace the static weekend band with a three-state hero (live, upcoming,
between weekends) driven by heroState() and existing page queries. Between-weekend
podium uses the existing race-hub endpoint for the last ingested race session
since championship hub does not expose finish positions.
Co-authored-by: Cursor <cursoragent@cursor.com>
Subscribe to TeamRadio on the F1 live feed, parse defensive capture snapshots and patches, and relay capped captures through live snapshots with the SessionInfo static path. Add the frontend CDN URL helper and ticker using one shared audio element.
Introduce a reusable SVG chart primitive for lap-by-lap cumulative time
delta vs a reference driver, with pure math in lib/delta.ts, dedicated
styles, null-lap gap handling, and Vitest coverage for math and rendering.
Co-authored-by: Cursor <cursoragent@cursor.com>
Extract stint timeline SVG from StrategyView into a data-source-agnostic
chart primitive with compound legend, lap-axis ticks, and hover titles.
StrategyView maps race-hub stint data into the new props contract.
Co-authored-by: Cursor <cursoragent@cursor.com>
Subscribe to SignalR Position.z and CarData.z, decode DEFLATE payloads, stream throttled positions over SSE, and render live car dots against cached track outline bounds with tap telemetry.
Alignment spike: no checked-in or locally cached real Position.z samples were available in this isolated worktree; verified both streams expose the official raw F1 X/Y/Z coordinate contract and implemented shared-bounds normalization against prefetched OpenF1 outlines. Fallback build-outline-from-stream was not taken.
Playwright remains out of scope for live rendering because BOXBOX_DISABLE_LIVE is used there; coverage is via parser, web handler/SSE, pure transform, and seeded component tests.
Six suite failures had rotted in: three race-hub specs targeted the
sub-tabbed Race Story UI deleted in the Phase 22 redesign, and the
command-center specs depended on the real OpenF1 calendar, whose focus
weekend drifted away from the seeded Monaco session as time passed.
The e2e/visual configs now point BOXBOX_OPENF1_BASE_URL (new override)
at an unreachable address so runs are hermetic and date-stable; the
race-hub specs were rewritten against the current RaceStoryCanvas UI,
and the one drifted mobile snapshot regenerated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Concurrent fan-outs could burst past the free-tier rate limit; callers
swallowed the 429s as missing data, and the championship hub cached the
resulting partial season for its full TTL (observed live: 8 of 22
rounds, zero wins/podiums/form).
- requestPacer spaces live requests (350ms anonymous, 100ms with key);
cache hits never wait
- get() retries 429s up to 3 times honouring Retry-After (capped 10s)
- hub tracks per-meeting fetch failures and caches incomplete
aggregates for only 2 minutes instead of 15min/24h
Verified against the live API: 2026 hub now reports all 22 rounds with
wins/podiums/form populated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fourth view on the championship page: assign P1-P10 per remaining
round and see projected standings update live, with position deltas
and mathematically alive/eliminated title states. Pure projection
logic in lib/simulator.ts (2025 points system, standard GPs only);
scenarios persist to localStorage per season. 169 frontend tests
passing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mocks /api/v1/live/state with a race snapshot to exercise the tower,
flag banner, weather strip, battle detection, stint history, and
driver pinning; also covers the no-session empty state.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Track status flag banner (green/yellow/SC/VSC/red, defensive mapping)
- Weather strip: air/track temp, humidity, wind with compass, rain badge
- Gap trend sparklines from a per-driver interval ring buffer
- Battle detection: consecutive cars within 1.0s chained and highlighted
- Stint history column with compound sequence per driver
- Pinnable drivers (max 3) with focus cards, persisted to localStorage
Pure logic lives in lib/gapHistory.ts and lib/battles.ts with unit
tests; 137 frontend tests passing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CLAUDE.md previously described only the TUI; it now covers the web
server (internal/web), React frontend, ingestion CLI, and the vitest/
playwright test commands. plan.md Phase 3 framing updated and the
championship hub recorded.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cold-cache hub loads previously issued ~70 sequential OpenF1 calls.
Per-meeting fetches now fan out 5-wide (order preserved), and the
aggregated response is cached in memory: 15min TTL for the current
season, 24h for past seasons.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New /api/v1/championship/hub endpoint aggregates official standings with
wins, podiums, poles, recent form, teammate head-to-head, and per-round
cumulative points. ChampionshipPage renders drivers, constructors, and
progression views.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>