# MVP Routing Implementation ## Overview Implement the Minimum Viable Product for the multimodal trip planning service, focusing on core routing functionality for single transport mode (trains) with maximum 1 transfer. This foundation will enable subsequent stages adding multimodality, deeper search, and station closure detection. **Problem it solves**: Users can search for train routes between cities with up to 1 transfer, with basic GeoJSON map visualization and proper API caching to respect Yandex.Schedules API quota limits. **Key benefits**: - Core routing engine within API quota constraints - Cache-aside pattern prevents API overuse - GeoJSON output enables immediate map visualization - TDD-guaranteed correctness for critical routing logic ## Context (from discovery) - **Files/components involved**: `internal/routing` (graph, search algorithm, MCT rules), `internal/yandex` (API client with rate limiter, retries, circuit breaker), `internal/cache` (Redis cache-aside), `cmd/api` (HTTP handlers), `cmd/cron` (station status detection) - **Related patterns**: Lazy graph expansion with hub stations, BFS/Dijkstra with depth limiting (4-5 transfers max), Pareto-front ranking (time, transfers, cost), cache-aside with multi-layer TTL - **Dependencies**: PostgreSQL for station/city directories, Redis for cache TTL, Yandex.Schedules API (`/search`, `/schedule`, `/nearest_stations`, `/stations_list`) ## Development Approach - **Testing approach**: TDD (tests first) - user preference confirmed - Complete each task fully before moving to the next - Make small, focused changes with tests - **CRITICAL: every task MUST include new/updated tests** for code changes in that task - tests are not optional - they are a required part of the checklist - write unit tests for new functions/methods - write unit tests for modified functions/methods - add new test cases for new code paths - update existing test cases if behavior changes - tests cover both success and error scenarios - **CRITICAL: all tests must pass before starting next task** - no exceptions - **CRITICAL: update this plan file when scope changes during implementation** - Run tests after each change - Maintain backward compatibility ## Testing Strategy - **Unit tests**: required for every task (see Development Approach above) - **E2E tests**: project has UI-based e2e tests considerations: - UI changes → add/update e2e tests in same task as UI code - Backend changes supporting UI → add/update e2e tests in same task - Treat e2e tests with same rigor as unit tests (must pass before next task) - Store e2e tests alongside unit tests (or in designated e2e directory) ## Progress Tracking - Mark completed items with `[x]` immediately when done - Add newly discovered tasks with ➕ prefix - Document issues/blockers with ⚠️ prefix - Update plan if implementation deviates from original scope - Keep plan in sync with actual work done ## What Goes Where - **Implementation Steps** (`[ ]` checkboxes): tasks achievable within this codebase - code changes, tests, documentation updates - **Post-Completion** (no checkboxes): items requiring external action - manual testing, changes in consuming projects, deployment configs, third-party verifications - **Checkbox placement**: Checkboxes belong only in Task sections (`### Task N:` or `### Iteration N:`). Do not put checkboxes in Success criteria, Overview, or Context — they cause extra loop iterations. ## Implementation Steps ### Task 1: Set up project structure and dependencies - [x] Initialize Go module (`go mod init trip-planner`) if not already done - [x] Add dependencies: `github.com/go-redis/redis/v8`, `github.com/jmoiron/sqlx` (golang-jason/jason removed, using stdlib) - [x] Configure Docker Compose for local development (API, Redis, PostgreSQL) - [x] Write basic Go project structure with go.mod, main.go, and internal packages - [x] Verify `go fmt ./...` and `go vet ./...` pass - [x] Run initial tests - must pass ### Task 2: Implement Yandex API client with rate limiter and circuit breaker - [x] Create `internal/yandex/client.go` with Yandex API wrapper - [x] Implement token bucket rate limiter (configurable TPS limit) - [x] Implement circuit breaker pattern (states: closed, open, half-open) - [x] Add retry with exponential backoff for transient errors - [x] Write tests for rate limiter (token consumption, refill rate) - [x] Write tests for circuit breaker (state transitions, trip to open state) - [x] Write tests for retry (success after backoff, exhaustion) - [x] Run tests - must pass before task 3 ### Task 3: Implement cache-aside layer for reference data and search results - [x] Create `internal/cache/store.go` with Redis cache interface - [x] Implement cache keys: `cities:{code}`, `stations:{id}`, `search:{from}:{to}:{date}` - [x] Implement cache-aside pattern: Redis → miss → Postgres/API → write-back to Redis - [x] Set TTL policies: cities/stations 30 days, search near-term 2-6 hours, search far-term 7 days - [x] Write tests for cache operations (get, set, invalidate, TTL expiry) - [x] Write tests for cache-aside pattern (cache hit, cache miss → API call → cache write) - [x] Run tests - must pass before task 4 ### Task 4: Implement routing graph and search algorithm (max 1 transfer) - [x] Create `internal/routing/graph.go` with Node and Edge types - [x] Implement Node types: Station, City; Edge kinds: Flight (real), Synthetic - [x] Build graph from station directory (Postgres + Redis cache) - [x] Implement BFS/Dijkstra search with 1-transfer depth limit - [x] Apply MCT (Minimum Connection Time) rules from transfer_rules table - [x] Write tests for graph construction (node/edge creation, directory loading) - [x] Write tests for search algorithm (successful 1-transfer route, no-route case, 2-transfer rejected) - [x] Write tests for MCT rule application (different node types, city tiers, check-in types) - [x] Run tests - must pass before task 5 ### Task 5: Implement API handlers for MVP endpoints - [x] Create `cmd/api/handlers.go` with HTTP handlers - [x] Implement `GET /v1/cities?query=` - city autocomplete from cached directory - [x] Implement `GET /v1/cities/{id}/stations` - city stations including neighbors if main closed - [x] Implement `POST /v1/routes/search` - body: from_city_id, to_city_id, date; response: Pareto-optimal routes (time, transfers) - [x] Implement `GET /v1/routes/{search_id}/{route_id}/geojson` - geometry for map visualization - [x] Implement `GET /v1/stations/{id}/status` - current station status - [x] Write handlers tests (success cases, error cases, input validation) - [x] Write integration tests (handler → cache → routing → API client flow) - [x] Run tests - must pass before task 6 ### Task 6: Implement cron job for station status detection - [x] Create `cmd/cron/station_status.go` daily cron job - [x] Query `/schedule` for each monitored station, count flights on upcoming dates - [x] Implement closure detection: N consecutive days of zero trips (N=3 recommended) → status `closed` - [x] Implement reactivation: status `active` when >0 trips appear - [x] Write tests for cron logic (status transition, zero-flight detection, reactivation) - [x] Run tests - must pass before task 7 ### Task 7: End-to-end integration and full test suite - [x] Write integration tests connecting all components: API → cache → routing → Yandex client - [x] Write synthetic timetable fixtures for routing tests (no real API calls) - [x] Run full test suite: `go test ./... -cover` - [x] Verify coverage meets project standard (80%+) - [x] Fix any failing tests - [x] Run `go fmt ./...` and `go vet ./...` - all issues must be fixed - [ ] Final verification: manual API endpoint testing with curl or Postman ## Post-Completion *Items requiring manual intervention or external systems - no checkboxes, informational only* **Manual verification**: - Test API endpoints with sample requests - Verify GeoJSON output format for map visualization - Test cache hit/miss scenarios - Test station status cron job behavior **External system updates**: - Docker Compose setup for local development (`docker-compose up -d`) - Gitea Actions workflow `.gitea/workflows/deploy.yml` CI/CD pipeline - Docker image build and push configuration - Watchtower configuration for auto-updates *Note: ralphex automatically moves completed plans to `docs/plans/completed/`*