40 KiB
Gas Stations Feature - Parallel Agent Execution Plan
Overview
This document contains agent definition prompts for implementing the Gas Stations feature using parallel execution. The backend API is complete; this plan focuses on frontend implementation, backend improvements, and integration.
Key Context
- Backend API: Fully implemented at
backend/src/features/stations/ - Frontend: Completely missing (needs full implementation)
- Database: Schema ready, needs migration execution
- Secrets: Google Maps API key exists in
secrets/app/google-maps-api-key.txt - Pattern: Follow K8s-aligned runtime secrets (not build-time env vars)
- Reference: Use
frontend/src/features/vehicles/as implementation pattern
Investigation Summary
- Backend has 4 complete API endpoints (search, save, get saved, delete)
- Database migration ready:
001_create_stations_tables.sql - External integration: Google Maps Places API client implemented
- No frontend code exists yet (placeholder route only)
- No tests exist (empty directories)
- No circuit breaker or cache cleanup implemented
Execution Phases
Phase 1 (Parallel - Infrastructure):
├─ Agent 1: Frontend-Secrets-Agent
└─ Agent 2: Backend-Reliability-Agent
Phase 2 (Foundation):
└─ Agent 3: Frontend-Foundation-Agent (depends on Agent 1)
Phase 3 (Parallel - Components):
├─ Agent 4: Components-Agent (depends on Agent 3)
└─ Agent 5: Map-Visualization-Agent (depends on Agent 3)
Phase 4 (Parallel - Pages):
├─ Agent 6: Desktop-Mobile-Agent (depends on Agents 4, 5)
└─ Agent 7: Integration-Agent (depends on Agents 4, 5)
Phase 5 (Testing):
└─ Agent 8: Testing-Documentation-Agent (depends on all previous)
Agent 1: Frontend-Secrets-Agent
Scope
Implement K8s-aligned runtime secrets pattern for frontend to load Google Maps API key from /run/secrets/ at container startup.
Prerequisites
- None (can start immediately)
Responsibilities
-
Create Frontend Config Loader Script
- File:
frontend/scripts/load-config.sh - Reads
/run/secrets/google-maps-api-key - Generates
/usr/share/nginx/html/config.jswith:window.CONFIG = { googleMapsApiKey: 'KEY_VALUE' } - Fallback to empty string if secret not found
- Logs success/failure for debugging
- File:
-
Update Frontend Dockerfile
- File:
frontend/Dockerfile - Copy
scripts/load-config.shinto image - Make script executable
- Add
SECRETS_DIRenvironment variable (default:/run/secrets) - Update CMD to:
["sh", "-c", "/app/load-config.sh && nginx -g 'daemon off;'"]
- File:
-
Update index.html
- File:
frontend/index.html - Add
<script src="/config.js"></script>before app bundle script tag - Ensure config loads synchronously before React initializes
- File:
-
Create Frontend Config Types
- File:
frontend/src/core/config/config.types.ts - Interface:
RuntimeConfig { googleMapsApiKey: string } - Extend Window interface:
declare global { interface Window { CONFIG: RuntimeConfig } } - Export typed accessor:
export function getConfig(): RuntimeConfig - Runtime validation: throw if required values missing
- File:
-
Update docker-compose.yml
- File:
docker-compose.yml - Add to
mvp-frontendservice volumes:- ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro - Add environment variable:
SECRETS_DIR: /run/secrets
- File:
-
Create Documentation
- File:
frontend/docs/RUNTIME-CONFIG.md - Explain runtime config pattern vs build-time
- How to add new runtime secrets
- K8s deployment notes
- Local development setup
- File:
Success Criteria
- Frontend container starts successfully
/usr/share/nginx/html/config.jsexists and contains API keygetConfig()returns typed RuntimeConfig object- No TypeScript errors
- Linters pass
Handoff Notes
Provides getConfig().googleMapsApiKey for Agent 5 (Map-Visualization-Agent) to use when loading Google Maps JavaScript API.
Agent 2: Backend-Reliability-Agent
Scope
Enhance backend reliability with circuit breaker, comprehensive tests, and cache cleanup job.
Prerequisites
- None (can start immediately)
Responsibilities
-
Add Circuit Breaker Pattern
- Install package: Add
"opossum": "^8.1.4"tobackend/package.json - File:
backend/src/features/stations/external/google-maps/google-maps.circuit-breaker.ts - Create circuit breaker factory function
- Configuration: 10s timeout, 50% error threshold, 30s reset timeout
- Update:
backend/src/features/stations/external/google-maps/google-maps.client.ts - Wrap
searchNearby()method with circuit breaker - Follow pattern from
backend/src/features/platform/(reference implementation) - Add circuit breaker events logging (open, close, halfOpen)
- Install package: Add
-
Backend Unit Tests
- File:
backend/src/features/stations/tests/unit/stations.service.test.ts- Test searchStations() with valid coordinates
- Test saveStation() with user_id isolation
- Test getSavedStations() returns only user's stations
- Test deleteSavedStation() removes correct station
- File:
backend/src/features/stations/tests/unit/stations.repository.test.ts- Test database queries with mock connection
- Test SQL injection protection
- Test constraint violations (duplicate saves)
- File:
backend/src/features/stations/tests/unit/google-maps.client.test.ts- Mock Google Maps API responses
- Test distance calculation (Haversine formula)
- Test photo URL generation
- Test error handling
- File:
-
Backend Integration Tests
- File:
backend/src/features/stations/tests/integration/stations.api.test.ts- Test all 4 endpoints with real database
- Test JWT authentication requirement
- Test request validation (Zod schemas)
- Test error responses (400, 401, 500)
- File:
backend/src/features/stations/tests/integration/saved-stations.flow.test.ts- Test complete workflow: search → save → retrieve → delete
- Test duplicate save handling
- Test user isolation (user A can't see user B's stations)
- File:
-
Test Fixtures
- File:
backend/src/features/stations/tests/fixtures/mock-stations.ts- Export sample Station objects (5-10 examples)
- Include various ratings, distances, prices
- File:
backend/src/features/stations/tests/fixtures/mock-google-response.ts- Mock Google Places API responses
- Include success and error cases
- File:
backend/src/features/stations/tests/fixtures/test-coordinates.ts- Sample coordinates for major cities
- Edge cases (equator, poles, date line)
- File:
-
Cache Cleanup Job
- File:
backend/src/features/stations/jobs/cache-cleanup.job.ts- Scheduled job using cron (daily at 2 AM)
- Delete
station_cacheentries older than 24 hours - Log metrics: rows deleted, execution time
- Error handling and retry logic
- Update:
backend/src/app.ts- Import and register cache cleanup job
- Add to scheduler (if exists) or create simple interval
- File:
-
Run Tests
- Execute:
cd backend && npm test -- features/stations - Verify 100% pass rate
- Check coverage (aim for >80%)
- Execute:
Success Criteria
- Circuit breaker implemented and tested
- All unit tests pass
- All integration tests pass
- Test coverage >80%
- Cache cleanup job registered
- Linters pass
- No TypeScript errors
Handoff Notes
Backend is now production-ready with reliability patterns. Tests provide safety net for future changes.
Agent 3: Frontend-Foundation-Agent
Scope
Create frontend foundation: types, API client, and React Query hooks for stations feature.
Prerequisites
- Depends on Agent 1 (needs
getConfig()function)
Responsibilities
-
Type Definitions
- File:
frontend/src/features/stations/types/stations.types.ts - Interface
Station:- placeId: string
- name: string
- address: string
- location: { latitude: number; longitude: number }
- rating?: number
- distance: number (meters)
- photoUrl?: string
- prices?: { fuel_type: string; price: number }[]
- Interface
SavedStationextends Station:- userId: string
- nickname?: string
- notes?: string
- isFavorite: boolean
- savedAt: string
- Interface
SearchRequest:- latitude: number
- longitude: number
- radius?: number (meters, default 5000)
- fuelType?: string
- Interface
SearchResponse:- stations: Station[]
- searchLocation: { latitude: number; longitude: number }
- Interface
SaveStationData:- placeId: string
- nickname?: string
- notes?: string
- isFavorite?: boolean
- File:
-
API Client
- File:
frontend/src/features/stations/api/stations.api.ts - Function
searchStations(request: SearchRequest): Promise<Station[]>- POST /api/stations/search
- Include JWT in Authorization header
- Return stations array
- Function
saveStation(data: SaveStationData): Promise<SavedStation>- POST /api/stations/save
- Include JWT in Authorization header
- Return saved station with full details
- Function
getSavedStations(): Promise<SavedStation[]>- GET /api/stations/saved
- Include JWT in Authorization header
- Return array of user's saved stations
- Function
deleteSavedStation(placeId: string): Promise<void>- DELETE /api/stations/saved/:placeId
- Include JWT in Authorization header
- Return nothing (204 No Content)
- Use axios instance from
frontend/src/core/api/client.ts - Proper error handling with try-catch
- Add request/response logging for debugging
- File:
-
React Query Hooks
- File:
frontend/src/features/stations/hooks/useStationsSearch.ts- Export
useStationsSearch()mutation hook - Accepts SearchRequest, returns Station[]
- Don't cache by default (location-specific queries)
- Loading/error states
- Export
- File:
frontend/src/features/stations/hooks/useSavedStations.ts- Export
useSavedStations()query hook - Query key:
['stations', 'saved'] - Auto-refetch on window focus
- Stale time: 5 minutes
- Loading/error states
- Export
- File:
frontend/src/features/stations/hooks/useSaveStation.ts- Export
useSaveStation()mutation hook - Accepts SaveStationData
- Optimistic update: add to saved list immediately
- On success: invalidate
['stations', 'saved']query - On error: rollback optimistic update
- Export
- File:
frontend/src/features/stations/hooks/useDeleteStation.ts- Export
useDeleteStation()mutation hook - Accepts placeId string
- Optimistic update: remove from saved list
- On success: invalidate
['stations', 'saved']query - On error: rollback optimistic update
- Export
- File:
frontend/src/features/stations/hooks/useGeolocation.ts- Export
useGeolocation()hook - Uses browser Geolocation API
- Returns:
{ position, loading, error, requestLocation } - Handle permission states: prompt, granted, denied
- Error handling: timeout, unavailable, permission denied
- Options: enableHighAccuracy, timeout, maximumAge
- Export
- File:
-
Utilities
- File:
frontend/src/features/stations/utils/distance.ts- Export
formatDistance(meters: number): string - Convert meters to miles with proper formatting
- Examples: "0.3 mi", "1.2 mi", "5.7 mi"
- Export
- File:
frontend/src/features/stations/utils/location.ts- Export
getCurrentPosition(): Promise<GeolocationPosition> - Wrapper around navigator.geolocation.getCurrentPosition
- Promisified API
- Error handling helpers
- Export
- File:
Success Criteria
- All type interfaces defined with proper TypeScript types
- API client functions work (test with curl against backend)
- All React Query hooks created with proper caching
- Geolocation hook handles all permission states
- No TypeScript errors
- Linters pass
- Utilities properly format data
Handoff Notes
Foundation is complete. Agent 4 and Agent 5 can now build components and map visualization using these types, API client, and hooks.
Agent 4: Components-Agent
Scope
Build all React components for stations feature (cards, lists, forms).
Prerequisites
- Depends on Agent 3 (needs types, hooks, API client)
Responsibilities
-
StationCard Component
- File:
frontend/src/features/stations/components/StationCard.tsx - Props:
- station: Station
- isSaved?: boolean
- onSave?: (placeId: string) => void
- onDelete?: (placeId: string) => void
- onSelect?: (station: Station) => void
- UI Elements:
- Material-UI Card component
- Station name (Typography variant="h6")
- Address (Typography variant="body2", color="text.secondary")
- Distance chip (Chip with LocationOnIcon)
- Rating stars (Rating component, if rating exists)
- Price display (Typography, if prices available)
- Photo thumbnail (CardMedia, if photoUrl exists)
- Save/Unsave IconButton (BookmarkIcon / BookmarkBorderIcon)
- Directions link (Button with OpenInNewIcon, opens Google Maps)
- Styling:
- Responsive: full width on mobile, fixed width on desktop
- Min height 44px for touch targets
- Hover effects on desktop
- Loading state while saving/deleting
- Follow pattern:
frontend/src/features/vehicles/components/VehicleCard.tsx
- File:
-
StationsList Component
- File:
frontend/src/features/stations/components/StationsList.tsx - Props:
- stations: Station[]
- loading?: boolean
- error?: Error | null
- onSaveStation?: (placeId: string) => void
- savedStationIds?: Set
- UI Elements:
- Grid container (responsive)
- Layout: 1 col mobile, 2 cols tablet, 3 cols desktop
- Loading state: Skeleton components (3-6 skeletons)
- Empty state: "No stations found. Try adjusting your search."
- Error state: Alert with retry option
- Map stations to StationCard components
- Styling:
- Gap between cards: 16px
- Smooth loading transitions
- Follow pattern:
frontend/src/features/vehicles/components/VehiclesList.tsx
- File:
-
SavedStationsList Component
- File:
frontend/src/features/stations/components/SavedStationsList.tsx - Props:
- savedStations: SavedStation[]
- loading?: boolean
- error?: Error | null
- onDelete: (placeId: string) => void
- onSelect?: (station: SavedStation) => void
- currentLocation?: { latitude: number; longitude: number }
- UI Elements:
- List container (vertical)
- Each item shows: nickname || name, address, distance from current location
- Notes preview (truncated to 50 chars)
- Favorite icon if isFavorite
- Delete IconButton with confirmation dialog
- Loading state: Linear progress bar
- Empty state: "No saved stations yet. Save stations from search results."
- Error state: Alert with error message
- Styling:
- List item min height: 64px
- Dividers between items
- Delete button always visible (not just on hover for mobile)
- Follow pattern: Material-UI List component examples
- File:
-
StationsSearchForm Component
- File:
frontend/src/features/stations/components/StationsSearchForm.tsx - Props:
- onSearch: (request: SearchRequest) => void
- loading?: boolean
- UI Elements:
- Form container (Card or Paper)
- "Use Current Location" Button (uses useGeolocation hook)
- OR divider
- Manual lat/lng inputs (TextField type="number")
- Radius slider (Slider: 1-25 miles, default 5, step 1)
- Fuel type select (optional, Select component)
- Search LoadingButton (shows loading state during search)
- Geolocation status indicator (permission state)
- Error display for geolocation failures
- Validation:
- Require either current location OR manual lat/lng
- Validate latitude: -90 to 90
- Validate longitude: -180 to 180
- Behavior:
- Click "Use Current Location" → request permission → populate hidden lat/lng
- Manual input disables current location
- Convert miles to meters for API (1 mile = 1609.34 meters)
- Styling:
- Compact form layout
- Responsive: stack vertically on mobile
- Clear visual feedback for loading states
- Follow pattern:
frontend/src/features/fuel-logs/components/FuelLogForm.tsx
- File:
-
Create Feature Index
- File:
frontend/src/features/stations/components/index.ts - Export all components for easy importing
- File:
Success Criteria
- All 4 components render without errors
- Components follow Material-UI design patterns
- Mobile responsive (test at 375px width)
- Touch targets minimum 44px
- Loading and error states work
- TypeScript types are correct
- Linters pass
- Components match vehicles feature pattern
Handoff Notes
Core components are ready. Agent 6 can now build pages using these components. Agent 7 can integrate with map visualization.
Agent 5: Map-Visualization-Agent
Scope
Implement Google Maps integration and StationMap component for visualizing stations on a map.
Prerequisites
- Depends on Agent 1 (needs
getConfig().googleMapsApiKey) - Depends on Agent 3 (needs Station types)
Responsibilities
-
Google Maps API Loader
- File:
frontend/src/features/stations/utils/maps-loader.ts - Function:
loadGoogleMapsAPI(): Promise<typeof google> - Load Google Maps JavaScript API dynamically
- Use runtime config:
getConfig().googleMapsApiKey - Include libraries:
libraries=places,geometry - Singleton pattern: only load once, return cached promise
- Error handling: throw if API key missing or script fails
- TypeScript: Use
@types/google.mapsfor types
- File:
-
Map Utilities
- File:
frontend/src/features/stations/utils/map-utils.ts - Function:
createStationMarker(station: Station, map: google.maps.Map, isSaved: boolean): google.maps.Marker- Create marker at station location
- Icon: blue pin for normal, gold star for saved
- Title: station name
- Clickable: true
- Function:
createInfoWindow(station: Station, isSaved: boolean, onSave: () => void, onDirections: () => void): google.maps.InfoWindow- Create info window with station details
- Content: name, address, rating, distance
- Action buttons: Save/Saved, Directions
- HTML string with proper escaping
- Function:
fitBoundsToMarkers(map: google.maps.Map, markers: google.maps.Marker[]): void- Calculate bounds containing all markers
- Fit map to bounds with padding
- Handle single marker case (zoom to 14)
- Function:
calculateCenter(stations: Station[]): google.maps.LatLng- Calculate geometric center of stations
- Return LatLng object
- File:
-
StationMap Component
- File:
frontend/src/features/stations/components/StationMap.tsx - Props:
- stations: Station[]
- savedStationIds?: Set
- center?: { latitude: number; longitude: number }
- zoom?: number
- currentLocation?: { latitude: number; longitude: number }
- onStationClick?: (station: Station) => void
- onSaveStation?: (placeId: string) => void
- State:
- map: google.maps.Map | null
- markers: google.maps.Marker[]
- infoWindow: google.maps.InfoWindow | null
- Behavior:
- Load Google Maps API on mount (useEffect)
- Initialize map in div container
- Create markers for all stations
- Current location marker (red pin, if provided)
- Click marker → open info window
- Click save button → call onSaveStation
- Click directions → open Google Maps in new tab
- Auto-fit bounds to show all markers
- Clean up markers on unmount
- Styling:
- Container: width 100%, height configurable (default 500px)
- Responsive: 300px height on mobile, 500px on desktop
- Map controls: zoom, pan, fullscreen
- Custom map styles (optional: subtle styling)
- Map Options:
- mapTypeControl: false
- streetViewControl: false
- fullscreenControl: true
- zoomControl: true
- Error Handling:
- Show error message if API fails to load
- Fallback: display stations in list if map unavailable
- File:
-
TypeScript Types
- Ensure
@types/google.mapsis infrontend/package.json - Add if missing:
"@types/google.maps": "^3.54.0"
- Ensure
-
Testing
- Test map loads with valid API key
- Test markers render for each station
- Test info windows open on marker click
- Test current location marker (red pin)
- Test directions link opens correct URL
- Test error handling (invalid API key)
Success Criteria
- Google Maps API loads successfully from runtime config
- StationMap component renders map
- Markers appear for all stations
- Color coding works (blue vs gold for saved)
- Info windows display correct station details
- Directions links work
- Current location marker shows if provided
- Responsive sizing works
- No TypeScript errors
- Linters pass
Handoff Notes
Map visualization is complete. Agent 6 can integrate StationMap into desktop and mobile pages.
Agent 6: Desktop-Mobile-Agent
Scope
Build desktop StationsPage and mobile StationsMobileScreen, update routing.
Prerequisites
- Depends on Agent 4 (needs all components)
- Depends on Agent 5 (needs StationMap)
Responsibilities
-
Desktop StationsPage
- File:
frontend/src/features/stations/pages/StationsPage.tsx - Layout:
- Container: Full width, Grid layout
- Left column (60%): StationMap (full height 600px)
- Right column (40%):
- StationsSearchForm at top
- Tabs component: "Search Results" | "Saved Stations"
- Tab 1 content: StationsList (scrollable)
- Tab 2 content: SavedStationsList (scrollable)
- State:
- searchResults: Station[] (from useStationsSearch)
- selectedStation: Station | null (for map focus)
- currentLocation: Position | null (from useGeolocation)
- activeTab: 0 | 1
- Hooks:
- useStationsSearch() for searching
- useSavedStations() for saved list
- useSaveStation() for saving
- useDeleteStation() for deleting
- useGeolocation() for current location
- Behavior:
- On search submit → call searchStations mutation
- On search success → update map with results
- On save station → add to saved list (optimistic)
- On delete station → remove from saved list (optimistic)
- Click station card → highlight on map (zoom to marker)
- Map marker click → highlight card and scroll into view
- Responsive:
- Breakpoint <960px: Stack vertically (map on top)
- Mobile view: Map 300px height, full width
- Loading States:
- Show loading spinner during search
- Skeleton loaders for saved stations
- Error Handling:
- Display error alerts for API failures
- Retry buttons where appropriate
- Follow pattern:
frontend/src/features/vehicles/pages/VehiclesPage.tsx
- File:
-
Mobile StationsScreen
- File:
frontend/src/features/stations/pages/StationsMobileScreen.tsx - Layout:
- BottomNavigation with 3 tabs: "Search", "Saved", "Map"
- Tab content area (full screen minus bottom nav)
- Tab 0 (Search):
- StationsSearchForm (compact mode, vertical layout)
- StationsList (vertical scroll, full width)
- Pull-to-refresh support (optional)
- Tab 1 (Saved):
- SavedStationsList (full screen)
- Swipe-to-delete gestures (optional enhancement)
- Empty state with illustration
- Tab 2 (Map):
- StationMap (full screen, 100vh minus nav)
- Floating action button (FAB) to return to search tab
- Current location button (FAB)
- State:
- activeTab: 0 | 1 | 2
- Same data state as desktop page
- Hooks:
- Same as desktop page
- Behavior:
- Tab switching persists scroll position
- Search → auto-switch to results view
- Save station → show snackbar confirmation
- Map marker click → open bottom sheet with station details
- Bottom Sheet:
- SwipeableDrawer component
- Shows station details
- Save/Delete buttons
- Directions link
- Touch Targets:
- All buttons minimum 44px
- Adequate spacing between interactive elements
- Safe Areas:
- Handle notched devices (safe-area-inset)
- Bottom navigation respects safe area
- Follow pattern:
frontend/src/features/vehicles/pages/VehiclesMobileScreen.tsx
- File:
-
Update Desktop Routing
- File:
frontend/src/App.tsx(around line 556) - Remove:
<Route path="/stations" element={<div>Stations (TODO)</div>} /> - Add:
<Route path="/stations" element={<StationsPage />} /> - Import:
import { StationsPage } from './features/stations/pages/StationsPage' - Verify route is inside ProtectedRoute wrapper (requires auth)
- File:
-
Update Mobile Routing
- File:
frontend/src/App.tsx(mobile routes section) - Add:
<Route path="/m/stations" element={<StationsMobileScreen />} /> - Import:
import { StationsMobileScreen } from './features/stations/pages/StationsMobileScreen' - Update mobile navigation items:
- Add stations icon and route to bottom nav
- Icon: PlaceRoundedIcon (already used in Layout.tsx)
- Ensure onClick navigates to
/m/stations
- File:
-
Feature Index
- File:
frontend/src/features/stations/index.ts - Export StationsPage and StationsMobileScreen
- Export all components for external use
- File:
-
Manual Testing
- Desktop: Navigate to /stations, test all functionality
- Mobile: Navigate to /m/stations, test all tabs
- Test on real mobile device or Chrome DevTools mobile emulation
- Verify responsive breakpoints work
- Verify touch targets are adequate (44px minimum)
Success Criteria
- Desktop page renders and functions completely
- Mobile screen renders with all 3 tabs
- Routing works for both desktop and mobile
- All components integrate properly
- Search, save, delete operations work
- Map updates when data changes
- Mobile touch targets meet 44px requirement
- Responsive design works at all breakpoints
- No TypeScript errors
- Linters pass
Handoff Notes
Desktop and mobile UIs are complete and fully functional. Users can now search, save, and view stations on both platforms.
Agent 7: Integration-Agent
Scope
Integrate stations feature with fuel logs using a station picker component.
Prerequisites
- Depends on Agent 4 (needs StationCard and search functionality)
- Depends on Agent 5 (needs map utilities)
Responsibilities
-
StationPicker Component
- File:
frontend/src/features/fuel-logs/components/StationPicker.tsx - Props:
- value: string (station name)
- onChange: (stationName: string) => void
- userLocation?: { latitude: number; longitude: number }
- label?: string
- error?: boolean
- helperText?: string
- UI Elements:
- Autocomplete component (Material-UI)
- TextField with LocationOnIcon adornment
- Dropdown shows: saved stations (group 1) + nearby stations (group 2)
- Option rendering:
- Primary text: station name
- Secondary text: distance + address
- Bookmark icon if saved
- FreeSolo mode: allow manual text entry
- Loading indicator while fetching stations
- Behavior:
- On focus → fetch saved stations immediately
- If userLocation provided → fetch nearby stations (5 mile radius)
- Show saved stations first (grouped, labeled "Saved Stations")
- Show nearby stations second (grouped, labeled "Nearby Stations")
- On select → call onChange with station name
- On manual input → call onChange with entered text
- Debounce: 300ms delay for search queries
- Data:
- Use useSavedStations() hook
- Use useStationsSearch() hook with userLocation
- Combine results: saved + nearby
- De-duplicate by placeId
- Error Handling:
- If geolocation fails → only show saved stations
- If API fails → fallback to manual text input
- Display error message in helper text
- Follow pattern: Material-UI Autocomplete with grouping
- File:
-
Update FuelLogForm
- File:
frontend/src/features/fuel-logs/components/FuelLogForm.tsx - Find: Current location input field (likely TextField or LocationInput component)
- Replace with: StationPicker component
- Props to pass:
- value: formData.stationName (or equivalent)
- onChange: (name) => setFormData({ ...formData, stationName: name })
- userLocation: from useGeolocation hook
- label: "Gas Station"
- error: Boolean(errors.stationName)
- helperText: errors.stationName
- Integration:
- Add useGeolocation hook to FuelLogForm
- Pass current position to StationPicker
- Maintain existing form validation
- Ensure backward compatibility (text input still works)
- File:
-
Enhance Fuel Log Display (Optional)
- File:
frontend/src/features/fuel-logs/components/FuelLogCard.tsx - If stationName is present → make it clickable
- On click → navigate to /stations with search for that name
- OR: Show station details in dialog
- Add LocationOnIcon next to station name
- Follow existing card styling
- File:
-
Add Geolocation to Fuel Logs
- Update FuelLogForm to request location permission on mount
- Store current location in component state
- Pass to StationPicker for nearby stations
- Show permission request UI if needed
- Handle permission denied gracefully (picker still works with saved stations)
-
Testing
- Test StationPicker in isolation (render component)
- Test selecting saved station
- Test selecting nearby station
- Test manual text entry (freeSolo)
- Test integration in FuelLogForm
- Test submitting fuel log with station
- Test geolocation permission flow
Success Criteria
- StationPicker component renders and functions
- Saved stations appear in dropdown
- Nearby stations appear in dropdown (if location available)
- Manual text entry works (fallback)
- FuelLogForm integrates StationPicker successfully
- Submitting fuel log with station works
- Backward compatible (existing fuel logs still display)
- Geolocation permission handled gracefully
- No TypeScript errors
- Linters pass
Handoff Notes
Fuel logs now integrate with stations feature. Users can select stations from saved or nearby lists when logging fuel, improving data consistency and user experience.
Agent 8: Testing-Documentation-Agent
Scope
Create comprehensive tests for all new code and complete documentation.
Prerequisites
- Depends on all previous agents (needs complete implementation)
Responsibilities
-
Backend Tests Verification
- Run:
cd backend && npm test -- features/stations - Verify all tests pass (from Agent 2)
- If failures: debug and fix
- Check coverage:
npm test -- --coverage features/stations - Ensure coverage >80%
- Run:
-
Frontend Component Tests
- File:
frontend/src/features/stations/__tests__/components/StationCard.test.tsx- Test rendering with all props
- Test save button click
- Test delete button click
- Test directions link
- File:
frontend/src/features/stations/__tests__/components/StationsList.test.tsx- Test rendering with stations array
- Test loading state (skeletons)
- Test empty state
- Test error state
- File:
frontend/src/features/stations/__tests__/components/SavedStationsList.test.tsx- Test rendering saved stations
- Test delete action
- Test empty state
- File:
frontend/src/features/stations/__tests__/components/StationsSearchForm.test.tsx- Test form submission
- Test validation (lat/lng ranges)
- Test current location button
- Test manual input
- File:
frontend/src/features/stations/__tests__/components/StationMap.test.tsx- Mock Google Maps API
- Test map initialization
- Test marker rendering
- Test info window
- Use React Testing Library
- Mock hooks with jest.mock()
- File:
-
Frontend API Tests
- File:
frontend/src/features/stations/api/__tests__/stations.api.test.ts- Test searchStations() - mock axios
- Test saveStation() - mock axios
- Test getSavedStations() - mock axios
- Test deleteSavedStation() - mock axios
- Test error handling (network errors, 401, 500)
- Verify request payloads
- Verify response parsing
- File:
-
Frontend Hook Tests
- File:
frontend/src/features/stations/hooks/__tests__/useStationsSearch.test.ts- Test mutation flow
- Test loading state
- Test success state
- Test error state
- File:
frontend/src/features/stations/hooks/__tests__/useSavedStations.test.ts- Test query flow
- Test cache behavior
- Test refetch logic
- File:
frontend/src/features/stations/hooks/__tests__/useSaveStation.test.ts- Test mutation with optimistic update
- Test cache invalidation
- Test rollback on error
- File:
frontend/src/features/stations/hooks/__tests__/useDeleteStation.test.ts- Test mutation with optimistic update
- Test cache invalidation
- Test rollback on error
- Use @testing-library/react-hooks
- Mock React Query client
- File:
-
Frontend Page Tests
- File:
frontend/src/features/stations/__tests__/pages/StationsPage.test.tsx- Test page renders
- Test search workflow
- Test save station workflow
- Test tab switching
- Test map interaction
- File:
frontend/src/features/stations/__tests__/pages/StationsMobileScreen.test.tsx- Test all 3 tabs render
- Test tab navigation
- Test search on mobile
- Test saved list on mobile
- File:
-
Integration Component Tests
- File:
frontend/src/features/fuel-logs/__tests__/components/StationPicker.test.tsx- Test rendering
- Test saved stations load
- Test nearby stations load (with geolocation)
- Test manual text entry
- Test selection callback
- Mock useStationsSearch and useSavedStations
- File:
-
E2E Tests (Optional but Recommended)
- File:
frontend/cypress/e2e/stations.cy.ts(or similar framework) - Test flows:
- Login → Navigate to Stations
- Search for nearby stations
- Save a station
- View saved stations
- Delete a station
- Navigate to Fuel Logs
- Select station from picker
- Submit fuel log
- Verify station name appears in log
- Use real backend in test environment
- Seed test data if needed
- File:
-
Run All Tests
- Backend:
cd backend && npm test - Frontend:
cd frontend && npm test - Verify 100% pass rate
- Fix any failures
- Backend:
-
Backend Documentation
- File:
backend/src/features/stations/docs/ARCHITECTURE.md- System design overview
- Data flow diagrams (text-based)
- External dependencies (Google Maps Places API)
- Caching strategy (Redis + PostgreSQL)
- Database schema with relationships
- Circuit breaker pattern explanation
- File:
backend/src/features/stations/docs/API.md- All endpoint documentation
- Request/response examples with curl commands
- Authentication requirements (JWT)
- Rate limits and quotas
- Error response formats
- File:
backend/src/features/stations/docs/TESTING.md- How to run tests
- Test database setup
- Writing new tests
- Coverage goals (>80%)
- CI/CD integration
- File:
backend/src/features/stations/docs/GOOGLE-MAPS-SETUP.md- Google Cloud Console setup
- Create project and enable Places API
- API key creation and restrictions
- Quota management and monitoring
- Cost estimation ($10-50/month typical)
- Security best practices (restrict by IP, referrer)
- File:
-
Frontend Documentation
- File:
frontend/src/features/stations/README.md- Feature overview
- Component hierarchy diagram (text-based)
- Hook usage examples
- Adding new functionality guide
- Runtime config pattern explanation
- Testing guide
- Integration with other features
- Update:
frontend/docs/RUNTIME-CONFIG.md(created by Agent 1)- Ensure complete and accurate
- File:
-
Update Main Documentation
- File:
docs/README.md- Add "Gas Stations" to features list
- Brief description: "Search and save gas stations using Google Maps"
- Link to backend docs:
backend/src/features/stations/docs/ - Link to frontend docs:
frontend/src/features/stations/README.md
- File:
backend/src/features/stations/README.md- Update with complete feature overview
- Configuration requirements
- API endpoints summary
- Testing instructions
- Deployment notes
- File:
-
Create User Guide (Optional)
- File:
docs/USER-GUIDE-STATIONS.md- How to search for stations
- How to save favorites
- How to use in fuel logs
- Screenshots (if possible)
- Troubleshooting common issues
- File:
Success Criteria
- All backend tests pass (100%)
- All frontend tests pass (100%)
- Test coverage >80% for new code
- All documentation files created
- Documentation is clear and accurate
- Code examples in docs work
- Main README updated
- No TypeScript errors
- Linters pass
Handoff Notes
Testing and documentation complete. Feature is production-ready. All code is tested, documented, and follows project standards.
Final Validation Checklist
After all agents complete, run final validation:
Build & Deploy
make rebuild # Rebuild all containers
make logs # Watch for errors
make migrate # Run database migrations
make test # Run all tests
Linting
cd backend && npm run lint && npm run type-check
cd frontend && npm run lint && npm run type-check
Manual Testing
- Desktop: Search stations at https://motovaultpro.com/stations
- Desktop: Save a station
- Desktop: View saved stations
- Desktop: Delete a station
- Desktop: Map visualization works
- Mobile: Navigate to stations (/m/stations)
- Mobile: All 3 tabs work
- Mobile: Touch targets are 44px minimum
- Fuel Logs: Station picker works
- Fuel Logs: Submit log with station
- Cross-browser: Chrome, Safari, Firefox
Performance
- Map loads in <2 seconds
- API responses <500ms
- Redis caching works (check logs)
- No memory leaks (React DevTools)
Security
- All endpoints require JWT auth
- User data isolation works (can't see other users' saved stations)
- SQL injection protected (prepared statements)
- API key not exposed to client (runtime config)
Documentation
- All docs created and accurate
- Code examples work
- Main README updated
- Google Maps setup guide complete
Standards (per CLAUDE.md)
- All linters pass with zero issues
- All tests pass
- Feature works end-to-end on desktop
- Feature works end-to-end on mobile
- Old code deleted (no placeholder routes)
- No emojis in code or docs
- Meaningful variable names used
Execution Commands
To execute agents in parallel using Claude Code:
Phase 1 (Parallel)
# Terminal 1
claude-code "Execute Agent 1: Frontend-Secrets-Agent as defined in GAS-STATION-AGENTS.md"
# Terminal 2
claude-code "Execute Agent 2: Backend-Reliability-Agent as defined in GAS-STATION-AGENTS.md"
Phase 2
# Wait for Phase 1 to complete
claude-code "Execute Agent 3: Frontend-Foundation-Agent as defined in GAS-STATION-AGENTS.md"
Phase 3 (Parallel)
# Terminal 1
claude-code "Execute Agent 4: Components-Agent as defined in GAS-STATION-AGENTS.md"
# Terminal 2
claude-code "Execute Agent 5: Map-Visualization-Agent as defined in GAS-STATION-AGENTS.md"
Phase 4 (Parallel)
# Terminal 1
claude-code "Execute Agent 6: Desktop-Mobile-Agent as defined in GAS-STATION-AGENTS.md"
# Terminal 2
claude-code "Execute Agent 7: Integration-Agent as defined in GAS-STATION-AGENTS.md"
Phase 5
# Wait for Phase 4 to complete
claude-code "Execute Agent 8: Testing-Documentation-Agent as defined in GAS-STATION-AGENTS.md"
Notes
- Each agent is designed to be autonomous and complete its scope independently
- Agents include clear prerequisites and dependencies
- Follow CLAUDE.md standards: no emojis, meaningful names, delete old code
- All implementations follow K8s-aligned patterns (runtime secrets, not build-time)
- Reference implementation: vehicles feature
- Testing is mandatory (per CLAUDE.md: must be 100% green)
- Mobile + Desktop requirement: all features work on both platforms
Estimated Total Time
- Phase 1: 8 hours (parallel: 8 hours)
- Phase 2: 4 hours
- Phase 3: 8 hours (parallel: 8 hours)
- Phase 4: 8 hours (parallel: 8 hours)
- Phase 5: 12 hours
Total: 40 hours elapsed (with parallelization) Total: 48 hours of work (across all agents)
Savings from parallelization: 8 hours (17% faster)