# TipSharks Project Plan

> Racing ratings and predictions platform — MVP-first roadmap.
> Last updated: 2026-05-07.
>
> **Completed this session:** 33 items across all three services (all Phase 1, 7 Phase 2, 5 Phase 3, 6 Phase 4, all 7 Phase 5).
>
> **Post-design review (2026-05-07):** Several "completed" Phase 2/3 items have critical user-facing gaps. See new Phase 5 below.

---

## Philosophy

**Functionality over polish.** The platform currently has three stable but disconnected services. The highest-value work is making them talk to each other so that real racing data flows from ingestion → ratings → mobile app. Visual improvements come after the system actually works end-to-end.

---

## Phase 1: MVP Foundation — Integration & Data Flow (Weeks 1–3)

Goal: Three services are connected. Data flows from TAB/HRNZ APIs through to the mobile app. A developer can `docker compose up` the entire stack.

- [x] **`[cross-cutting]` Docker Compose for full stack**
  - **Scope:** Root-level `docker-compose.yml` bringing up PostgreSQL, Redis, MongoDB, Jaeger, Prometheus, Grafana, tab-api-ingest, tipsharks-elo-api, and tipsharks-client backend.
  - **Why:** Every integration task needs a reproducible local environment. This is the unblocker for everything else.
  - **Effort:** M

- [x] **`[cross-cutting]` Define shared domain models / OpenAPI contract**
  - **Scope:** Agree on `Race`, `Runner`, `Meeting`, `Dividend` shapes across all three services. Publish as an OpenAPI spec or shared Pydantic/Prisma-compatible schema.
  - **Why:** Currently each service defines its own types. Integration is guesswork without a contract.
  - **Effort:** M

- [x] **`[cross-cutting]` GitHub Actions CI pipeline**
  - **Scope:** Lint, typecheck, and test for all three services on every PR.
  - **Details:** tab-api-ingest (npm ci, lint, test, build), tipsharks-elo-api (uv sync, ruff, mypy, pytest), tipsharks-client (npm/yarn ci, eslint, tsc --noEmit, jest).
  - **Why:** Prevents regressions as integration work touches multiple services.
  - **Effort:** M

- [x] **`[cross-cutting]` Root README with architecture diagram and setup**
  - **Scope:** System diagram, port map, env var checklist, `docker compose up` instructions.
  - **Why:** Onboarding cost is currently high. A clear README enables contributors and future operators.
  - **Effort:** S

- [x] **`[cross-cutting]` End-to-end smoke test**
  - **Scope:** Script that starts all services, creates a race via ingest, triggers Elo rating, and fetches it through the client backend.
  - **Why:** The only way to know integration works is to exercise the full pipeline.
  - **Effort:** M

- [x] **`[tipsharks-elo-api]` Consume data from tab-api-ingest**
  - **Scope:** Replace standalone TAB client/HRNZ scraper ingestion with a consumer that reads from the ingest service's database or REST API. This is the central integration task.
  - **Why:** Eliminates duplicate ingestion logic and makes ingest the single source of truth for raw data.
  - **Effort:** L

- [x] **`[tipsharks-client/backend]` Replace mock data with real elo-api calls**
  - **Scope:** Switch `generate_mock_data()` to HTTP calls against `tipsharks-elo-api` (`GET /v1/races`, `GET /v1/ratings/{entity_id}`, `GET /v1/predictions/{race_id}`). Add HTTP client wrapper with timeout and retry.
  - **Why:** The mobile app currently shows fake data. This makes it real.
  - **Effort:** L

- [x] **`[tipsharks-client/backend]` Implement persistent MongoDB read path**
  - **Scope:** Repository pattern for races, tips, schedules, and results. Ensure GET endpoints query MongoDB instead of regenerating mocks or holding state in memory.
  - **Why:** Provides stable state between requests and across restarts.
  - **Effort:** M

---

## Phase 2: MVP Core — Trust & Functionality (Weeks 4–6)

Goal: Users can browse real races, get tips based on real ratings, and trust the results. The product is usable.

- [x] **`[tipsharks-client]` Implement authentication (JWT or stable anonymous IDs)**
  - **Scope:** Add signup/login endpoints (or at minimum generate a stable anonymous UUID per install), JWT middleware, and secure password hashing. Update frontend to pass real user context.
  - **Why:** Hardcoded `default_user` means tips leak across users and there's no privacy or personalization.
  - **Effort:** L

- [x] **`[tipsharks-elo-api]` Data backfill pipeline**
  - **Scope:** Ingest 12–24 months of TAB historical results, run `recompute`, and validate output Brier scores.
  - **Why:** Ratings based on minimal history are unreliable. Historical depth is required for credible predictions.
  - **Effort:** XL

- [x] **`[tipsharks-elo-api]` Add pagination to list endpoints`**
  - **Scope:** Add `limit`/`offset` query parameters and include `next`/`prev` links or JSON `meta` block.
  - **Why:** Unbounded `/races` and `/ratings` responses will break as data grows.
  - **Effort:** M

- [x] **`[tipsharks-elo-api]` Web UI: Model accuracy dashboard**
  - **Scope:** New page showing Elo prediction performance vs. market odds over the last 30 days. Win/place accuracy, Brier score trend, ROI simulation.
  - **Why:** This is the single most trust-building feature for the web UI. It answers "should I believe these ratings?"
  - **Effort:** M

- [x] **`[tipsharks-elo-api]` Web UI: Race card view**
  - **Scope:** A compact, scannable race card showing runners with ratings, barriers, and predicted probabilities. Responsive (grid on desktop, cards on mobile).
  - **Why:** Bridges the web/mobile gap. Makes the web UI useful for quick pre-race decisions.
  - **Effort:** M

- [x] **`[tipsharks-client]` Mobile: Tip confidence visualization**
  - **Scope:** Replace text "HIGH CONFIDENCE" with a visual meter (segmented bar or radial indicator). Add micro-stat when available (e.g. "73% historical accuracy at High confidence").
  - **Why:** Visual metaphors create instant comprehension. Text labels feel cheap.
  - **Effort:** S

- [x] **`[tipsharks-client]` Mobile: Runner visual hierarchy**
  - **Scope:** Enhance `RunnerRow` with mini-bars for rating contribution, barrier advantage, or form trend.
  - **Why:** Users need to see *why* a runner is ranked, not just the rank itself.
  - **Effort:** S

- [x] **`[tipsharks-client]` Mobile: Smart primary CTA**
  - **Scope:** If next race starts in <5 minutes, auto-surface with urgency styling (countdown, pulsing border). Otherwise show "Browse Today's Races".
  - **Why:** Reduces friction for the most common user action.
  - **Effort:** XS

- [x] **`[tipsharks-elo-api]` Add WebSocket support for live race updates**
  - **Scope:** FastAPI WebSocket endpoint (`/ws/races/{race_id}`) pushing odds changes and result updates.
  - **Why:** Polling is inefficient for live race tracking. Enables real-time mobile and web experiences.
  - **Effort:** L

- [x] **`[tipsharks-elo-api]` Add repository tests with real PostgreSQL**
  - **Scope:** Docker-based test DB. Integration tests for `RaceRepository` and `RatingRepository`.
  - **Why:** 13 test files exist but none test the DB layer against a real database. Integration gaps will break in production.
  - **Effort:** M

- [x] **`[tab-api-ingest]` Add integration tests for scheduler execution paths**
  - **Scope:** Mock `Date.now()` and BullMQ to assert correct job enqueueing for each scheduler.
  - **Why:** Scheduler logic (timing, race selection, deduplication) is currently untested.
  - **Effort:** M

---

## Phase 3: UX Polish — Design & Experience (Weeks 7–8)

Goal: The product looks and feels credible. Visual identity signals that the algorithm underneath deserves trust.

- [x] **`[cross-cutting]` Create shared design tokens**
  - **Scope:** `design-tokens.json` at monorepo root defining colors (dark navy + cyan accent), spacing, typography scale, and radii.
  - **Why:** Unlocks parallel web and mobile styling work. Prevents "what hex was that again?"
  - **Effort:** XS

- [x] **`[tipsharks-client]` Mobile dark mode**
  - **Scope:** `ThemeProvider` in `_layout.tsx`, replace hardcoded hex with token references, toggle in Profile tab.
  - **Why:** Racing users are at the track, in bars, early mornings. Light mode is physically uncomfortable.
  - **Effort:** M

- [x] **`[tipsharks-elo-api]` Web UI visual overhaul (Bootstrap → Tailwind CDN dark theme)**
  - **Scope:** Replace Bootstrap 5 CDN with Tailwind CDN + custom config. Dark theme by default. Restyle navbar, cards, tables, and chart colors.
  - **Why:** The current Bootstrap UI looks like a weekend project. A dark, refined stylesheet makes it look like a serious analytics platform. No framework migration needed.
  - **Effort:** M

- [x] **`[tipsharks-elo-api]` Add Alembic migration tests (up and down)**
  - **Scope:** Test that runs `alembic upgrade head`, `alembic downgrade base`, and asserts schema parity.
  - **Why:** Prevents irreversible migrations.
  - **Effort:** S

- [x] **`[tipsharks-elo-api]` Improve Web UI styling beyond Bootstrap defaults**
  - **Scope:** Custom theme, mobile responsiveness, refined spacing. (This becomes the polish pass after Tailwind is in place.)
  - **Why:** Generic Bootstrap hurts credibility.
  - **Effort:** M

- [x] **`[tipsharks-client]` Add deep linking for races and tips**
  - **Scope:** Expo Linking configuration so users can share direct URLs to races or tip cards.
  - **Why:** Organic growth via sharing.
  - **Effort:** S

- [x] **`[tipsharks-client]` Add offline support (cache races and tips locally)**
  - **Scope:** AsyncStorage or SQLite cache for races and saved tips with TTL.
  - **Why:** Users at the track often have poor connectivity.
  - **Effort:** M

- [x] **`[tipsharks-client/backend]` Implement notification service (SMS/Email)**
  - **Scope:** Integrate Twilio (SMS) and SendGrid/Resend (email). Opt-in, rate limiting. `POST /notifications/send` and `POST /notifications/schedule`.
  - **Why:** Scheduled tips and race reminders are core value props.
  - **Effort:** M

---

## Phase 4: Scale & Ops (Ongoing)

Goal: Production confidence, performance, and operational maturity.

- [x] **`[cross-cutting]` Production deployment config**
  - **Scope:** `docker-compose.prod.yml`, `.env.prod.example`, documented secrets strategy.
  - **Effort:** L

- [x] **`[tipsharks-elo-api]` Add performance regression tests**
  - **Scope:** `pytest-benchmark` for `recompute` on fixed dataset and `GET /predictions/{race_id}` under load. Fail CI on >10% regression.
  - **Effort:** M

- [x] **`[tipsharks-elo-api]` Add automated cron scheduling for background jobs**
  - **Scope:** APScheduler-driven loop in the API process or separate worker container running ingest/recompute/scrape on schedule.
  - **Effort:** M

- [x] **`[tipsharks-elo-api]` Add rate-limiting per-user (not just global)**
  - **Scope:** Authenticated users get their own quota buckets.
  - **Effort:** S

- [x] **`[tab-api-ingest]` Add request/response logging middleware with correlation IDs**
  - **Scope:** Human-readable request logs with trace IDs. Complements existing Jaeger traces.
  - **Effort:** S

- [x] **`[tab-api-ingest]` Add dead-letter queue for failed BullMQ jobs**
  - **Scope:** Durable failure audit path beyond retries.
  - **Effort:** S

- [x] **`[tipsharks-client]` Add analytics instrumentation (screen views, tip conversions)**
  - **Scope:** Understand which tips users act on.
  - **Effort:** M

- [x] **`[cross-cutting]` Add unified health check endpoint for all services**
  - **Scope:** Single `/health` or `/ready` probe reporting on ingest, elo-api, and client backend status.
  - **Effort:** S

---

## Phase 5: Post-Design Review — Critical User-Facing Gaps (Current)

Goal: Fix broken user journeys that were masked by "completion" of earlier phases. The platform has stable services but fractured UX.

- [x] **`[tipsharks-client]` Wire Profile preferences to race feeds**
  - **Scope:** `getRaces()` calls in `TipsHome` and `RacesScreen` currently ignore `preferred_racing_types`, `preferred_race_classes`, `preferred_distances`, and `preferred_conditions`.
  - **Why:** Users configure filters in Profile but see unfiltered races everywhere. Broken promise.
  - **Effort:** S

- [x] **`[tipsharks-client]` Build Race Detail screen**
  - **Scope:** New screen between race list and tip-builder showing runner list, form, barriers, weights, odds, and Elo-derived win probabilities.
  - **Why:** Tapping a race currently forces the user straight into tip generation. No chance to review runners first.
  - **Effort:** M

- [x] **`[tipsharks-client]` Complete authentication flow**
  - **Scope:** Connect login screen to backend JWT, store token, fetch real profile, display actual name/email in Profile tab, implement logout.
  - **Why:** Profile permanently shows hardcoded "Racing Fan / guest@tipster.app". JWT interceptors exist but auth store is not wired to UI.
  - **Effort:** M

- [x] **`[tipsharks-client]` Add saved tip deletion UI**
  - **Scope:** Swipe-to-delete or long-press menu on Recent Tips cards. Backend `DELETE /tips/saved/{id}` already exists.
  - **Why:** Home shows "Recent Tips" but no way to remove them. "View history" text is a dead touch target.
  - **Effort:** S

- [x] **`[tipsharks-client/backend]` Replace mock data with real Elo API calls**
  - **Scope:** Switch `generate_mock_data()` to proxy `GET /v1/races`, `GET /v1/predictions/{race_id}`, and `GET /v1/ratings/*` from `tipsharks-elo-api`. Add fallback to mock only on timeout/error.
  - **Why:** Mobile app currently shows fake data. The Elo API exists and has data backfill capability but the client backend never calls it.
  - **Effort:** L

- [x] **`[cross-cutting]` Establish shared design tokens**
  - **Scope:** `design-tokens.json` at monorepo root: colors (cyan accent, dark navy, racing-type palette), spacing, typography scale, border radii.
  - **Why:** Web UI (Bootstrap Icons, 8px radius) and mobile (Ionicons, 12–16px radius) have divergent visual languages.
  - **Effort:** S

- [x] **`[tipsharks-elo-api]` Web UI: migrate icons and align with mobile palette**
  - **Scope:** Replace Bootstrap Icons with Phosphor Icons or Heroicons. Adopt mobile's racing-type color coding (blue/purple/green for Gallops/Trots/Dogs). Bump card radius to 12px.
  - **Why:** Web UI looks dated compared to mobile. Shared icon family and color coding reduces cognitive load.
  - **Effort:** S

---

## Completed Items (Retained for Reference)

### P0: Critical Fixes
- [x] Fix race lookup by ID returning wrong race
- [x] Fix tip generation ignoring race_id
- [x] Fix mock data regeneration on every request
- [x] Fix Elo `k_base` default value mismatch
- [x] Replace hardcoded `default_user` with real user context
- [x] Implement missing BullMQ worker layer
- [x] Create `src/routes/` for ingested data access
- [x] Fix empty `tests/` directory (frontend)

### P1: Core Integration
- [x] Add API versioning (`/v1/` prefix)
- [x] Automate HRNZ club code refresh

### P2: Feature Completion
- [x] Add pre-commit hooks (lint, typecheck, format)

### P3: Enhancement & Polish
- [x] Add `ETag` and `Last-Modified` headers to rating endpoints
- [x] Add export endpoints (CSV/Parquet for ratings and predictions)

### Cross-Cutting
- [x] Add dependency update automation (Renovate)

---

## Effort Legend

| Symbol | Meaning |
|--------|---------|
| XS | ≤ 2 hours |
| S | Half day |
| M | 1–2 days |
| L | 3–5 days |
| XL | 1–2 weeks |

---

## How to Use This File

1. **Work phase-by-phase, top-to-bottom.** Do not start Phase 3 polish until Phase 1 integration is complete.
2. **Within a phase, pick by dependency order.** Docker Compose and shared models come before data pipeline. Data pipeline comes before client backend integration.
3. **Check the box** when a task is merged to main.
4. **Update effort estimates** if scope changes during implementation.
5. **Add new items** at the bottom of the relevant phase; do not renumber.
