Skip to content

Instantly share code, notes, and snippets.

@jeremylongshore
Last active March 6, 2026 05:28
Show Gist options
  • Select an option

  • Save jeremylongshore/560e68aaba736763ca4a43d3bf83d14e to your computer and use it in GitHub Desktop.

Select an option

Save jeremylongshore/560e68aaba736763ca4a43d3bf83d14e to your computer and use it in GitHub Desktop.
Braves Booth Intelligence — Full Technical Breakdown

Braves Booth Intelligence: Operator-Grade System Analysis

For: DevOps Engineer Generated: 2026-03-05 Version: 801566a (main)


1. Executive Summary

Business Purpose

Braves Booth Intelligence is a real-time AI-powered broadcast operations dashboard for the Atlanta Braves radio booth. It surfaces player stats, AI-generated narratives, matchup history, and situational intelligence to announcers and producers during live MLB games — with sub-6-second data latency from MLB's live feed to the broadcast booth.

The system serves a single, high-stakes use case: Ben Ingram and his co-hosts open a browser tab before first pitch and get pre-populated, LLM-enhanced talking points for every at-bat. The dashboard updates automatically as the game progresses — batter changes, pitcher substitutions, score updates — without any manual intervention.

The tech stack is a three-service monorepo (Fastify backend, React frontend, FastAPI Python service) deployed on Google Cloud Platform. The core architectural constraint is zero network dependencies during live broadcast for all cached data — SQLite over Firestore, pre-generation over live LLM calls, graceful degradation over hard failures.

Current status: Production-deployed at braves-booth.web.app with CI/CD automation. Two known bugs (co-host panel blank due to missing profile data, data staleness due to no lineup-phase awareness) and several enhancement opportunities documented. Estimated monthly cost: $3-8/month on Cloud Run.

Operational Status Matrix

Environment Status Uptime Target Release Cadence
Production (Cloud Run + Firebase) Active Best-effort (broadcast hours) Push-to-main auto-deploy
Local Dev (Docker Compose) Active N/A Continuous
Staging Not implemented

Technology Stack

Category Technology Version Purpose
Frontend React + Vite + Tailwind CSS 18.3 / 6.0 / 4.2 SPA dashboard
Backend Fastify + TypeScript 5.2 / 5.7 API server, SSE, orchestration
Python FastAPI + pybaseball 0.115+ / 2.2.7 Statcast analytics pipeline
Database SQLite (better-sqlite3) 12.6 Local persistence (WAL mode)
Cache node-cache 5.1 In-memory TTL cache
AI Vertex AI (Gemini 2.5 Pro) 1.10 Narrative generation
Hosting Firebase Hosting CDN + SPA routing
Compute Cloud Run Backend + Python services
CI/CD GitHub Actions Test + deploy pipeline
Registry Artifact Registry Docker images
Auth Workload Identity Federation GitHub → GCP keyless auth

System Health Score: 74/100

Category Score Notes
Architecture 9/10 Clean separation, event-driven, three-tier cache
Code Quality 9/10 Strict TypeScript, no any types, zero TODOs
Test Coverage 6/10 Backend 76 tests (good), Frontend 10 tests (thin), Python 16 tests
Operations 5/10 No monitoring, no alerting, no IaC, no staging
Security 8/10 No secrets in code, parameterized SQL, input validation, WIF auth
Documentation 8/10 11 indexed docs, comprehensive CLAUDE.md, good README
Resilience 7/10 Circuit breaker, graceful degradation, but no retry/backoff on MLB API

2. System Architecture

Architecture Diagram

┌─ BROWSER ────────────────────────────────────────────────────────────────┐
│  React 18 + Vite 6 + Tailwind v4                                       │
│  ┌──────────────────────────────────────────────────────────────────┐   │
│  │ GameContext (SSE → state provider)                               │   │
│  │  ├── TopNav (co-host selector, mode toggle, clock)              │   │
│  │  ├── GameStateBar (score, inning, count, bases)                 │   │
│  │  ├── DashboardGrid (3-column)                                   │   │
│  │  │   ├── BatterCard (stats, splits, narrative, back-of-card)    │   │
│  │  │   ├── PitcherCard (tonight line, pitch mix, velo, K stats)   │   │
│  │  │   └── RightSidebar                                           │   │
│  │  │       ├── SituationalPanel (weather, umpire, park factors)   │   │
│  │  │       ├── OnDeckPanel (on-deck + in-hole)                    │   │
│  │  │       ├── CohostPanel (broadcaster profile)                  │   │
│  │  │       └── BullpenPanel (defense mode)                        │   │
│  │  ├── Ticker (scrolling league scores)                           │   │
│  │  └── QueryBar (Cmd+K AI Q&A)                                   │   │
│  └──────────────────────────────────────────────────────────────────┘   │
│          │ SSE                    │ REST                                 │
└──────────┼────────────────────────┼──────────────────────────────────────┘
           │                        │
           ▼                        ▼
┌─ BACKEND (Fastify 5 + TypeScript, Cloud Run) ───────────────────────────┐
│                                                                          │
│  GUMBO Poller ──► Event Bus ──► SSE Route ──► Connected Clients         │
│  (5s poll)        (EventEmit)   (/api/events)                           │
│                                                                          │
│  Game State    Prefetch Engine    AI Service (Vertex AI)                 │
│  (in-memory)   (pregame.ts)      (Gemini 2.5 Pro)                      │
│                                                                          │
│  ┌────────────────── Caching Layer ──────────────────────┐              │
│  │  L1: node-cache (in-memory, per-key TTL 60s-24h)     │              │
│  │  L2: SQLite (WAL mode, narrative_log + player_stats)  │              │
│  │  L3: API fetch + AI generation (on-demand)            │              │
│  └───────────────────────────────────────────────────────┘              │
└────────┬─────────────────────────────────┬───────────────────────────────┘
         │                                 │
         ▼                                 ▼
┌─ MLB Stats API ──────┐      ┌─ PYTHON SERVICE (FastAPI, Cloud Run) ─────┐
│ statsapi.mlb.com     │      │  Circuit Breaker (3 fail → 60s cooldown)  │
│ ├── Live Feed (GUMBO)│      │  TTL Caches (30min statcast, 1hr matchup) │
│ ├── Player Stats     │      │  pybaseball → Statcast analytics          │
│ ├── Schedule         │      │  Endpoints:                                │
│ ├── Standings        │      │  ├── /statcast/batter/:id                 │
│ └── Boxscore         │      │  ├── /statcast/pitcher/:id                │
├──────────────────────┤      │  ├── /splits/:id/:type                    │
│ Open-Meteo (weather) │      │  ├── /matchup/:batterId/:pitcherId        │
├──────────────────────┤      │  └── /batch/prefetch                      │
│ Vertex AI (Gemini)   │      └───────────────────────────────────────────┘
└──────────────────────┘

Technology Stack (Detailed)

Layer Technology Version Purpose Config
Frontend React 18.3.1 Component UI
Frontend Vite 6.0.0 Build + HMR vite.config.ts
Frontend Tailwind CSS 4.2.1 Utility CSS globals.css
Frontend TypeScript 5.7 Type safety tsconfig.json (strict)
Backend Fastify 5.2.0 HTTP + SSE server
Backend @google-cloud/vertexai 1.10.0 Gemini 2.5 Pro GCP_PROJECT_ID env
Backend better-sqlite3 12.6.2 Embedded database DB_PATH env
Backend node-cache 5.1.2 In-memory TTL cache cache.ts TTL constants
Backend Pino 9.6.0 Structured logging LOG_LEVEL env
Backend TypeScript 5.7 Type safety tsconfig.json (strict)
Python FastAPI 0.115+ Statcast API
Python pybaseball 2.2.7+ Baseball Reference scraper
Python cachetools 5.3+ TTL caches cache.py
Python httpx 0.28+ Async HTTP client
Infra Docker Compose Local dev orchestration docker-compose.yml
Infra Cloud Run Production compute deploy.yml
Infra Firebase Hosting CDN + SPA + API proxy firebase.json
Infra Artifact Registry Container images us-east4
Infra GitHub Actions CI/CD ci.yml + deploy.yml

Data Flow Summary

MLB GameFeed API (5s poll)
  → GUMBO Poller (parse + diff)
    → Event Bus (batter-change, pitcher-change, score-change, game-state)
      → SSE Route (/api/events) → Browser EventSource
        → useGameState hook → GameContext → All Dashboard Panels

Prefetch Pipeline (boot + batter/pitcher changes):
  → Roster extraction from boxscore
    → Parallel stat fetch (concurrency=5): MLB API + Python statcast
      → AI narrative generation (Gemini 2.5 Pro)
        → Three-tier cache (memory → SQLite → serve)

3. Directory Analysis

Project Structure

braves/
├── .github/workflows/
│   ├── ci.yml                    # Lint + typecheck + test (all 3 services)
│   └── deploy.yml                # Python → Backend → Frontend deploy
├── 000-docs/                     # 12 indexed architecture/design docs
│   ├── 001-PP-PROD-*.md          # PRD v1.0
│   ├── 002-AT-ARCH-*.md          # Technical infrastructure spec
│   ├── 003-AT-DSGN-*.md          # Design system
│   ├── 004-RL-RSRC-*.md          # Research brief
│   ├── 005-WA-WFLW-*.md          # Workflow automation
│   ├── 006-RL-RSRC-*.md          # Deep research findings
│   ├── 007-AT-ARCH-*.md          # Worldmonitor baseline audit
│   ├── 008-PM-PLAN-*.md          # Git workflow
│   ├── 009-TQ-TEST-*.md          # Testing strategy
│   ├── 010-AT-ARCH-*.md          # Architect review (7 critical fixes)
│   ├── 011-AT-DSGN-*.md          # Frontend gap analysis
│   └── 012-AA-AUDT-*.md          # This document
├── backend/
│   ├── src/
│   │   ├── server.ts             # Fastify bootstrap + prefetch hooks
│   │   ├── db.ts                 # SQLite init (4 tables, WAL mode)
│   │   ├── lib/                  # config, cache, http-client, logger
│   │   ├── services/             # gumbo-poller, game-state, event-bus,
│   │   │                         # pregame, ai, mlb-stats, matchup,
│   │   │                         # player-db, cohost, umpire
│   │   └── routes/               # 12 route files (SSE, batter, pitcher,
│   │                             # narrative, query, game, matchup,
│   │                             # ticker, weather, umpire, cohost, health)
│   ├── data/                     # Umpire defaults JSON
│   ├── scripts/                  # find-game.ts, test-endpoints.ts
│   ├── Dockerfile                # Multi-stage Node 22 build
│   ├── package.json              # 8 runtime + 7 dev dependencies
│   ├── tsconfig.json             # Strict mode, ES2022, ESNext modules
│   ├── eslint.config.js          # Flat config, typescript-eslint
│   └── .env.example              # All env vars documented
├── frontend/
│   ├── src/
│   │   ├── components/           # 16 components (layout, panels, shared)
│   │   ├── hooks/                # useGameState, useFetch, usePolling
│   │   ├── context/              # GameContext (SSE state provider)
│   │   ├── lib/                  # api.ts (all backend calls + normalizers)
│   │   ├── types/                # api.ts, events.ts
│   │   └── styles/               # globals.css (design system)
│   ├── Dockerfile                # Node 22 dev-mode container
│   ├── package.json              # 3 runtime + 10 dev dependencies
│   ├── tsconfig.json             # Strict, noUncheckedIndexedAccess
│   ├── vite.config.ts            # React + Tailwind plugins, API proxy
│   └── eslint.config.js          # Flat config, react-hooks plugin
├── python/
│   ├── app/
│   │   ├── main.py               # FastAPI app, 5 routers
│   │   ├── routes/               # health, statcast, splits, matchup, batch
│   │   ├── services/             # statcast (pybaseball wrapper), prefetch
│   │   └── lib/                  # circuit_breaker, cache
│   ├── tests/                    # 4 test files, 16 tests
│   ├── scripts/                  # prefetch.py, test-statcast.py
│   ├── Dockerfile                # Python 3.12-slim
│   ├── requirements.txt          # 5 runtime dependencies
│   └── pyproject.toml            # Ruff + pytest config
├── docker-compose.yml            # 3 services + booth-data volume
├── firebase.json                 # Hosting + API rewrite to Cloud Run
├── .firebaserc                   # Project: braves-booth
├── Makefile                      # 20+ targets (dev, test, lint, deploy)
├── team-config.json              # Braves team config (id, stadium, colors)
├── CLAUDE.md                     # AI assistant guidance
└── README.md                     # Quick start + architecture overview

Key Directories

  • backend/src/services/: Core business logic — GUMBO poller, prefetch orchestrator, AI narrative engine, MLB Stats API client, dual-source matchup resolver. 9 service files, all with clean separation of concerns.
  • backend/src/routes/: 12 HTTP route files. All async with try/catch error handling. Parameterized SQL queries throughout (no injection risk).
  • frontend/src/components/panels/: 6 dashboard panels. BatterCard is largest (1,140+ lines). Each panel manages its own data fetching via hooks.
  • python/app/lib/: Circuit breaker (CLOSED→OPEN→HALF_OPEN state machine) and TTL caches (statcast 30min, splits/matchup 1hr).

4. Operational Reference

Deployment Workflows

Local Development

Prerequisites:

  • Docker Desktop (or Docker Engine + Compose)
  • Node.js 22+ (for running individual services)
  • Python 3.12+ (for Python service outside Docker)
  • GCP credentials: gcloud auth application-default login (for Vertex AI)

Setup:

git clone <repo>
cd braves
cp backend/.env.example backend/.env
make dev  # Docker Compose up --build (all 3 services)

Verification:

curl http://localhost:3001/api/health       # Backend health
curl http://localhost:8001/health            # Python health
open http://localhost:5173                    # Frontend dashboard
make test-sse                                # Test SSE stream
make find-game                               # Find today's gamePk

Individual services (without Docker):

make dev-backend      # cd backend && npm run dev (tsx watch, port 3001)
make dev-frontend     # cd frontend && npm run dev (vite, port 5173)
make dev-python       # uvicorn app.main:app --reload --port 8001

Production Deployment

Pre-flight checklist:

  • All CI checks pass (lint, typecheck, test for all 3 services)
  • No .env files staged
  • team-config.json updated if team config changed
  • GCP service account permissions verified

Execution: Automatic on push to main via .github/workflows/deploy.yml:

  1. Python service → Build Docker image → Push to Artifact Registry → Deploy to Cloud Run (braves-booth-python, 1GB/1CPU, 0-2 instances)
  2. Backend service → Copy team-config.json into build context → Build → Push → Deploy to Cloud Run (braves-booth-backend, 512MB/1CPU, 0-2 instances, --no-cpu-throttling, --session-affinity, 1hr timeout)
  3. Frontend → Vite build with VITE_SSE_URL → Copy to public/ → Deploy to Firebase Hosting
  4. Health checkcurl https://braves-booth.web.app/api/health

Rollback protocol:

# List Cloud Run revisions
gcloud run revisions list --service braves-booth-backend --region us-east4
gcloud run revisions list --service braves-booth-python --region us-east4

# Rollback to previous revision
gcloud run services update-traffic braves-booth-backend \
  --to-revisions=<previous-revision>=100 --region us-east4

# Firebase Hosting rollback
firebase hosting:channel:list  # or revert via Firebase Console

Monitoring & Alerting

Aspect Current State Recommendation
Health checks Backend + Python /health endpoints Add Cloud Run uptime checks
Logging Pino structured JSON (backend), stdout (Python) Cloud Logging already captures; add log-based alerting
Metrics None Add Cloud Run metrics dashboard (latency, errors, instances)
Error tracking None Add Sentry or Cloud Error Reporting
Dashboards None Create Cloud Monitoring dashboard
Alerting None Cloud Monitoring alerts on 5xx rate, latency P95, instance count

Incident Response

Severity Definition Response Time Playbook
P0 Dashboard down during live broadcast Immediate Check Cloud Run logs, restart service, rollback if needed
P1 SSE disconnections during game 5 min Check Cloud Run instance health, verify --no-cpu-throttling
P2 Stale data / missing narratives 15 min Check Vertex AI quota, Python circuit breaker state, cache TTLs
P3 Non-game-day issues Next business day Standard triage

5. Security & Access

IAM

Role Resource Purpose Auth Method
bb-backend-runtime Cloud Run backend Runtime SA for Vertex AI access GCP SA
bb-deploy Cloud Run + Artifact Registry CI/CD deployment WIF (GitHub Actions)
GitHub Actions GCP via WIF Keyless auth for deploys Workload Identity Federation
Developer Local Vertex AI via ADC gcloud auth application-default login

Secrets Management

Secret Storage Rotation
GCP credentials Workload Identity Federation (no keys) N/A (keyless)
GitHub secrets (WIF_PROVIDER, WIF_SERVICE_ACCOUNT) GitHub encrypted secrets Manual
No API keys for MLB Stats API N/A (public API) N/A
No database passwords N/A (SQLite, no auth) N/A

Security Posture

  • No hardcoded secrets in codebase (verified: grep for API keys, passwords, tokens — zero results)
  • Parameterized SQL throughout (no SQL injection vectors)
  • Input validation on all numeric IDs (parseInt + isNaN guard)
  • CORS properly configurable (permissive in dev, strict in production)
  • No eval(), exec(), or Function() in any service
  • No unsafe deserialization patterns
  • AbortSignal timeouts on all external HTTP calls (3-5s)
  • Python Dockerfile runs as root (recommendation: add USER nobody)

6. Cost & Performance

Monthly Costs (Estimated)

Resource Cost Notes
Cloud Run (Backend) $1-3/mo ~1,100 req/game x 162 games, min instances=0
Cloud Run (Python) $1-3/mo Prefetch bursts, idle between games
Firebase Hosting $0 Free tier (< 10 GB/mo bandwidth)
Artifact Registry $0.10/mo Image storage
Vertex AI (Gemini) $1-2/mo ~20 narratives/game, pre-generated
Total $3-8/mo

Performance Baseline

Metric Target Current
MLB feed poll → SSE push < 6s ~5s (5s poll interval)
Batter change → narrative ready < 3s 1.5-2.5s (prefetched)
Dashboard cold load < 2s ~1.5s (Firebase CDN)
AI narrative generation < 5s 2-4s (Gemini 2.5 Pro)
Python statcast fetch < 10s 3-8s (pybaseball, circuit-broken)

Cache TTLs

Key TTL Justification
Schedule (today's games) 60s Frequent checks during game
Player stats 5 min Season stats don't change fast
Standings 5 min Updated between games
Matchup (H2H) 1 hour Historical data, stable
Narrative 5 min (memory) / persistent (SQLite) Re-serve within game
Umpire 24 hours Doesn't change
Weather 5 min Updated frequently enough
Statcast (Python) 30 min Pre-game data, stable
Splits (Python) 1 hour Historical, stable

7. Current State Assessment

What's Working

  • Real-time pipeline: GUMBO poller → EventEmitter → SSE streaming works reliably with 5s latency
  • Three-tier caching: Memory → SQLite → API/AI generation prevents redundant fetches
  • Prefetch system: Boot-time + reactive prefetch (on-deck, pitcher-change) keeps data warm
  • AI narratives: Gemini 2.5 Pro generates quality radio-ready talking points in 2-4s
  • Dual-source matchup: MLB Stats API + Python statcast in parallel with graceful partial failure
  • Circuit breaker: Python service protected from pybaseball hangs/failures
  • CI/CD: Automated testing + deployment on every push to main
  • Code quality: Strict TypeScript (no any), zero TODO/FIXME, comprehensive error handling
  • Backend test suite: 76 tests covering routes, services, state management, AI parsing
  • Security: No secrets in code, WIF auth, parameterized SQL, input validation
  • Documentation: 11 indexed docs + comprehensive CLAUDE.md + README

Areas Needing Attention

  • Co-host panel broken: Missing backend/data/cohost-profiles/*.json files — directory doesn't exist
  • Data staleness: No lineup-phase awareness — system fetches once at boot, never re-fetches when lineups are posted (~90 min before first pitch)
  • Frontend test coverage thin: Only 10 tests across 4 files (20% component coverage). BatterCard (1,140 lines), PitcherCard, useGameState all untested
  • No error boundary: Single failing React component crashes entire dashboard
  • No monitoring/alerting: No Sentry, no Cloud Monitoring alerts, no dashboards
  • No Infrastructure-as-Code: Cloud Run config is imperative CLI in deploy.yml, not reproducible
  • No staging environment: Code goes straight from local → production
  • Python Dockerfile runs as root: Missing USER directive
  • Placeholder UI panels: BullpenPanel shows "Data loading...", umpire section is placeholder
  • No retry/backoff on MLB API: GUMBO poller catches errors but doesn't retry — waits 5s for next poll
  • pybaseball no explicit timeout: Thread executor calls to pybaseball can hang indefinitely

Immediate Priorities

  1. [Critical] Fix co-host panel — Create backend/data/cohost-profiles/ with broadcaster JSON files. Impact: Feature F8 (Co-Host Intelligence) completely non-functional. Fix: 30 minutes.

  2. [Critical] Fix data staleness — Add lineup-watch poller that re-triggers prefetch when lineups are confirmed. Impact: Announcers see stale/missing data at first pitch. Fix: 1-2 days.

  3. [High] Add React error boundary — Single component failure shouldn't crash the dashboard during a live broadcast. Impact: Runtime resilience. Fix: 2 hours.

  4. [High] Add Cloud Monitoring alerts — 5xx error rate, latency P95, instance scaling. Impact: No visibility into production health. Fix: Half day.

  5. [Medium] Increase frontend test coverage — Prioritize BatterCard, PitcherCard, useGameState. Impact: Regression risk on core components. Fix: 2-3 days.

  6. [Medium] Add pybaseball timeout — Wrap thread executor calls with explicit timeout to prevent indefinite hangs. Impact: Worker thread exhaustion during live game. Fix: 1 hour.

  7. [Low] Add staging environment — Firebase preview channel + separate Cloud Run services. Impact: No pre-production validation. Fix: Half day.


8. Quick Reference

Command Map

Capability Command Notes
Start all services make dev Docker Compose, all 3 services
Start backend only make dev-backend tsx watch, port 3001
Start frontend only make dev-frontend Vite, port 5173
Start Python only make dev-python uvicorn --reload, port 8001
Run all tests make test Frontend + Backend + Python
Run backend tests make test-backend vitest
Run frontend tests make test-frontend vitest
Run Python tests make test-python pytest
Run single backend test cd backend && npx vitest run src/routes/batter.test.ts
Run single frontend test cd frontend && npx vitest run src/components/Panel.test.tsx
Run single Python test cd python && python -m pytest tests/test_statcast.py -v
Lint all make lint ESLint + Ruff
Typecheck all make typecheck tsc (frontend + backend)
Find today's game make find-game Prints gamePk
Test SSE stream make test-sse curl to /api/health
Test API endpoints make test-endpoints Backend script
Prefetch game data make prefetch GAME_PK=12345 Python prefetch script
Deploy preview make deploy-preview Frontend-only to Firebase
Full clean make clean Docker down -v, rm node_modules
Check backend health curl localhost:3001/api/health
Check Python health curl localhost:8001/health
View Cloud Run logs gcloud run logs read --service braves-booth-backend --region us-east4
List Cloud Run revisions gcloud run revisions list --service braves-booth-backend --region us-east4

Critical URLs

Resource URL
Production https://braves-booth.web.app
Backend API https://braves-booth.web.app/api/health
GCP Console https://console.cloud.google.com/run?project=braves-booth
Artifact Registry https://console.cloud.google.com/artifacts?project=braves-booth
Firebase Console https://console.firebase.google.com/project/braves-booth
GitHub Repo (private repo)
CI/CD GitHub Actions tab
Showcase Gist https://gist.github.com/jeremylongshore/07e15be57e39b73c0b6f486bbfad1b8a

API Endpoint Map

Endpoint Method Purpose
/api/events GET (SSE) Real-time game state stream
/api/game/live GET Current game state + poller status
/api/game/today GET Today's game discovery
/api/game/start-polling POST Manual poller start
/api/game/stop-polling POST Stop polling
/api/batter/:id/stats GET Batter stats (SQLite-backed)
/api/pitcher/:id/stats GET Pitcher stats (SQLite-backed)
/api/matchup/:batterId/:pitcherId GET Head-to-head (dual-source)
/api/narrative POST AI narrative (3-tier cache)
/api/query POST Free-form AI Q&A
/api/ticker GET Scores + standings
/api/weather GET Venue weather
/api/umpire/:id GET Umpire data
/api/cohost GET List broadcaster profiles
/api/cohost/:id GET Single broadcaster profile
/api/health GET Health check

First-Week Checklist

  • Clone repo, run make dev, verify all 3 services start
  • Run make test — all 102 tests pass
  • Run make lint + make typecheck — clean
  • Access https://braves-booth.web.app — verify production is live
  • gcloud auth application-default login — Vertex AI access
  • Review 000-docs/010-AT-ARCH-architect-review.md — 7 critical decisions
  • Review 000-docs/001-PP-PROD-*.md — PRD and feature map (F1-F9)
  • Understand SSE flow: GUMBO → Event Bus → SSE Route → Frontend
  • Understand caching: node-cache TTLs → SQLite persistence → API/AI generation
  • Trigger a manual prefetch: make prefetch GAME_PK=<gamePk>
  • Review Cloud Run config in deploy.yml (timeout, session-affinity, cpu-throttling)

9. Recommendations Roadmap

Week 1 — Stabilization

Goal: Fix known bugs, add critical resilience.

  1. Fix co-host panel — Create broadcaster profile JSON files, verify panel renders
  2. Fix data staleness — Add lineup-watch poller with game-phase state machine
  3. Add React error boundary — Wrap dashboard in error boundary with graceful fallback
  4. Add pybaseball timeout — Explicit 30s timeout on thread executor calls
  5. Add Cloud Monitoring dashboard — Cloud Run metrics (latency, errors, instances)

Measurable outcome: Dashboard shows correct co-host data, pre-game data refreshes when lineups are posted, no single-component crashes.

Month 1 — Foundation

Goal: Production hardening and test coverage.

  1. Frontend test coverage to 60% — BatterCard, PitcherCard, useGameState, OnDeckPanel
  2. Add staging environment — Firebase preview channel + separate Cloud Run services
  3. Add Cloud Monitoring alerts — 5xx rate > 1%, latency P95 > 10s, zero instances
  4. Add Sentry error tracking — Frontend + backend
  5. Season bootstrap endpointPOST /api/admin/bootstrap-season for 162-game pre-population
  6. Python Dockerfile hardening — Non-root user, health check, multi-stage build

Measurable outcome: 60%+ frontend test coverage, staging environment for pre-production testing, alerting on production issues.

Quarter 1 — Strategic

Goal: Full pre-population pipeline, operational maturity.

  1. Complete pre-game intelligence pipeline — Intel cards pre-generated for all lineup batters
  2. Daily morning check job — Cloud Scheduler → weather + status verification
  3. Infrastructure-as-Code — Terraform for Cloud Run + Firebase + IAM
  4. Bullpen panel implementation — Real-time reliever tracking from boxscore API
  5. Player news integration — Search API for recent 30-day player news
  6. Performance dashboard — Cache hit rates, AI generation latency, prefetch timing

Measurable outcome: Announcer opens dashboard before first pitch and everything is already populated. Zero LLM calls during broadcast (except pitcher changes/pinch hitters).


Appendices

A. Glossary

Term Definition
GUMBO MLB's real-time game data feed (Game Update Management for Baseball Operations)
SSE Server-Sent Events — one-way server-to-client streaming over HTTP
gamePk MLB's unique game identifier (integer)
Intel Card Pre-generated player intelligence package (stats + bio + narrative + matchup)
Circuit Breaker Pattern that stops calling a failing service after N consecutive failures
WAL Write-Ahead Logging — SQLite journal mode for concurrent reads during writes
Prefetch Proactively fetching data before it's needed (on-deck/in-hole batters)
WIF Workload Identity Federation — keyless auth between GitHub Actions and GCP

B. SQLite Schema

CREATE TABLE cohost_profiles (
  id TEXT PRIMARY KEY,
  name TEXT NOT NULL,
  profile_json TEXT NOT NULL,
  updated_at INTEGER NOT NULL
);

CREATE TABLE narrative_log (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  game_date TEXT NOT NULL,
  game_pk INTEGER NOT NULL,
  batter_id INTEGER NOT NULL,
  pitcher_id INTEGER NOT NULL,
  input_payload TEXT NOT NULL,
  output_json TEXT NOT NULL,
  latency_ms INTEGER,
  created_at INTEGER NOT NULL
);

CREATE TABLE player_stats (
  player_id INTEGER NOT NULL,
  season TEXT NOT NULL,
  player_type TEXT NOT NULL,
  name TEXT, team TEXT, position TEXT,
  number INTEGER, bats TEXT, throws TEXT,
  stats_json TEXT NOT NULL,
  splits_json TEXT, last_season_json TEXT,
  updated_at INTEGER NOT NULL,
  PRIMARY KEY (player_id, season, player_type)
);

CREATE TABLE preferences (
  key TEXT PRIMARY KEY,
  value TEXT NOT NULL
);

C. Environment Variables

# Backend
PORT=3001                                    # Server port
HOST=0.0.0.0                                # Bind address
NODE_ENV=development                         # dev/production
LOG_LEVEL=info                               # Pino log level
DB_PATH=./booth.db                           # SQLite file path
TEAM_CONFIG_PATH=../team-config.json         # Team config file
MLB_STATS_API_BASE=https://statsapi.mlb.com/api/v1
MLB_GAME_FEED_BASE=https://statsapi.mlb.com/api/v1.1
PYBASEBALL_SERVICE_URL=http://localhost:8001 # Python service URL
CORS_ORIGIN=                                 # Production: https://braves-booth.web.app
GCP_PROJECT_ID=braves-booth                  # Vertex AI project
GCP_LOCATION=us-east4                        # Vertex AI region

# Frontend (build-time)
VITE_BACKEND_URL=http://localhost:3001       # Dev proxy target
VITE_SSE_URL=                                # Production: Cloud Run URL

# Python
# No env vars — config is code-level

D. Test Inventory

Service Test Files Test Count Framework Status
Backend 19 files 76 tests Vitest All pass
Frontend 4 files 10 tests Vitest + Testing Library All pass
Python 4 files 16 tests pytest All pass
Total 27 files 102 tests All pass

E. Dependency Versions

Backend (package.json):

@fastify/cors: ^11.2.0    @google-cloud/vertexai: ^1.10.0
better-sqlite3: ^12.6.2   dotenv: ^17.3.1
fastify: ^5.2.0            node-cache: ^5.1.2
pino: ^9.6.0               pino-pretty: ^13.0.0
typescript: ~5.7.0         vitest: ^4.0.0

Frontend (package.json):

react: ^18.3.1             react-dom: ^18.3.1
tailwindcss: ^4.2.1        @tailwindcss/vite: ^4.2.1
vite: ^6.0.0               vitest: ^4.0.18
typescript: ~5.7.0

Python (requirements.txt):

fastapi>=0.115.0,<1.0      uvicorn[standard]>=0.34.0,<1.0
pybaseball>=2.2.7,<3.0     cachetools>=5.3.0,<6.0
httpx>=0.28.0,<1.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment