Mobilna aplikacja dla biegaczy rekreacyjnych przygotowujących się do dystansów od pierwszego kilometra do maratonu. Produkt łączy plan treningowy, wykonane aktywności ze Stravy oraz dynamiczne zalecenia dotyczące posiłków, nawodnienia i regeneracji.
Status: dokumentacja startowa do budowy PoC/MVP z użyciem GitHub Copilot w Visual Studio Code.
- jedna aplikacja mobilna na iOS i Android,
- React Native + TypeScript + Expo Development Build,
- backend jako modularny monolit w .NET 10,
- PostgreSQL,
- wdrożenie przez Docker Compose na Mikrusie 3.5 lub 4.1,
- integracja ze Stravą jako pierwsze źródło treningów,
- jeden tester w fazie PoC,
- mały koszt utrzymania i łatwa migracja na większą infrastrukturę,
- AI wspiera użytkownika, ale nie podejmuje samodzielnie decyzji medycznych.
- Przeczytaj wizję produktu.
- Przeczytaj zakres MVP.
- Zapoznaj się z architekturą i wdrożeniem na Mikrusie.
- Uruchom w Copilot Chat prompt
bootstrap-repositoryz katalogu.github/prompts. - Realizuj zadania kolejno z katalogu
docs/tasks.
running-fuel/
├── apps/
│ └── mobile/ # React Native / Expo
├── services/
│ └── api/ # .NET 10 modular monolith
├── tests/
│ ├── api.integration/
│ └── architecture/
├── infra/
│ ├── docker/
│ ├── scripts/
│ └── mikrus/
├── docs/
├── .github/
│ ├── workflows/
│ ├── instructions/
│ └── prompts/
├── AGENTS.md
└── README.md
- Task 001 (bootstrap) — zrealizowany: szkielet
apps/mobile(Expo Router, design system, ekran „Dzisiaj” na danych mockowych) iservices/api(health checks, brak modułów biznesowych), Docker Compose, testy. - Task 002 (konto, profil, onboarding) — etap 1 — zrealizowany w zakresie opisanym w ADR-011–014: deweloperska sesja (
/api/v1/dev/session, EF Core + Postgres, SecureStore po stronie mobile), chronione trasy ((auth)/(protected)), pełny onboarding (dane podstawowe, doświadczenie biegowe, cel, żywienie, bezpieczeństwo, podsumowanie) z lokalnym zapisem postępu, ekran profilu biegacza, przygotowanie architektury połączenia ze Stravą (7 stanów UI, endpointy statusu/connect/sync z jawnym trybem wyłączonym). Pełne ASP.NET Core Identity (rejestracja e-mail/hasło) i realne OAuth Stravy pozostają do zrobienia — patrzdocs/tasks/002-auth-profile-onboarding.mdidocs/tasks/004-strava-integration.md. - Task 003 (kalendarz treningowy) — zrealizowany w zakresie opisanym w ADR-017: deterministyczny, idempotentny seed 12-tygodniowego planu przygotowań do półmaratonu (EF Core + Postgres), ustrukturyzowane kroki treningu (
WorkoutStep, nie wolny tekst), endpointyplan/weeks/{weekStart}/workouts/{id}zcomplete/skip/restore/PATCH, ekran Trening (pasek dni, zakładki Plan/Wykonane, karta najbliższego treningu z wizualizacją interwałów SVG, podsumowanie tygodnia), ekran szczegółów treningu z ręcznym ukończeniem/pominięciem/przywróceniem/edycją, oraz ekran Dzisiaj czytający to samo źródło danych treningowych co ekran Trening. Tworzenie nowego treningu przez użytkownika, dedykowana akcja „Anuluj” i dopasowanie do aktywności Stravy pozostają do zrobienia — patrzdocs/tasks/003-training-calendar.mdidocs/tasks/004-strava-integration.md. - Task 004 (integracja ze Stravą) — zrealizowany w zakresie opisanym w ADR-018: serwerowy OAuth 2.0 (backend jako jedyny posiadacz tokenów, szyfrowanie przez ASP.NET Core Data Protection z kluczami na trwałym wolumenie), import ostatnich 30 dni aktywności i synchronizacja przyrostowa/webhookowa przez generyczną tabelę zadań
BackgroundJobw PostgreSQL (FOR UPDATE SKIP LOCKED, bez Redis/RabbitMQ), deterministyczny silnik dopasowania aktywność↔trening (ActivityWorkoutMatcher) z automatycznym ukończeniem powyżej progu pewności i sugestią poniżej, ochrona ręcznie ukończonych treningów przed nadpisaniem, ekran integracji Strava i szczegółu zaimportowanej aktywności z potwierdzeniem/zmianą dopasowania, plakietki źródła ukończenia na ekranach Dzisiaj/Trening/szczegół treningu, odłączenie z zachowaniem historii Runth. Prawdziwe uwierzytelnienie OAuth wobec API Stravy nie zostało zweryfikowane w tym środowisku (brak realnych poświadczeń) — testy backendu używająFakeStravaApiClient; patrzdocs/tasks/004-strava-integration.mdpo kroki ręcznej weryfikacji. Integracja z Garminem pozostaje do zrobienia. - Task 005 (Running Nutrition Engine) — zrealizowany w zakresie opisanym w ADR-019: deterministyczny silnik żywieniowy (klasyfikacja dnia, cel energetyczny wg formuły Mifflin-St Jeor + bonus treningowy zależny wyłącznie od typu dnia i czasu trwania — nigdy z kalorii Stravy, makroskładniki periodyzowane, cel nawodnienia, timing posiłków względem godziny treningu), wersjonowana konfiguracja reguł (
nutrition-engine-v1), 28 oryginalnych przepisów bez scrapingu z deterministycznym filtrowaniem dietetycznym (twarde wykluczenia alergenów/nietolerancji/nielubianych produktów) i doborem, wersjonowany append-only plan dnia z leniwą idempotentną regeneracją odporną na współbieżność (porównanie hashy wejść treningowych/profilowych), zamiana posiłku i zmiana porcji, ekran Żywienie i szczegół posiłku, integracja z ekranem Dzisiaj i szczegółem treningu (żywienie okołotreningowe). Pełny dziennik jedzenia/logowanie wody i lista zakupów pozostają do zrobienia — patrzdocs/tasks/005-nutrition-engine.md. - Task 006 (Dziennik i lista zakupów) — zrealizowany w zakresie opisanym w ADR-020: dziennik (własny wpis posiłku ze statusem szacunkowym, nawodnienie, żywienie na biegu, samopoczucie), lista zakupów z agregacją jednostek i kategoryzacją sklepową, przeglądalna/filtrowalna biblioteka przepisów z ulubionymi (zamyka FR-RCP-003). Mobile działa lokalnie-najpierw (SQLite + jeden generyczny outbox) — Dziennik i Lista zakupów są w pełni użyteczne offline; konflikty synchronizacji (współbieżna edycja) są zawsze rozwiązywane jawnym wyborem użytkownika, nigdy cichym nadpisaniem. Arkusz Szybkie Dodaj i check-in samopoczucia na ekranie Dzisiaj są teraz realnie podłączone (wcześniej wizualne stuby). Backend 204/204 i mobile 254/254 testów — patrz
docs/tasks/006-journal-shopping.md. - Task 007 (Postępy i raport tygodniowy) — zrealizowany w zakresie opisanym w ADR-021: kilometraż tygodniowy, realizacja planu, RPE, energia, realizacja posiłków okołotreningowych, trend masy (z możliwością ukrycia) i deterministyczny raport tygodniowy — wszystko bez AI. Cała agregacja jest czystą, testowaną funkcją mobile nad już istniejącymi endpointami Treningu/Żywienia/Dziennika (rozszerzenie jawnej zasady „weekly math lives on mobile” z Task 003) — jedyny nowy zasób backendu to pomiar masy, lokalny-najpierw jak Dziennik. Każda uśredniona/porównawcza wartość wymaga co najmniej dwóch punktów danych, inaczej pokazuje jawne „za mało danych” zamiast twierdzenia z jednego pomiaru; żadna obserwacja nigdy nie łączy dwóch różnych metryk w jedno zdanie. Backend 214/214 i mobile 280/280 testów — patrz
docs/tasks/007-progress-weekly-report.md. - Task 008 (Bezpieczne funkcje AI) — zrealizowany w zakresie opisanym w ADR-022:
IAiProviderz domyślnymDisabledAiProvider(brak klucza nigdy nie przerywa startu ani nie blokuje głównych funkcji, ten sam wzorzec co Strava) iAnthropicAiProvider(Anthropic Claude przez oficjalny SDK, structured outputs, jedna próba naprawy przy błędzie transportu), cztery endpointy (explain-recommendation,summarize-week,search-recipes,parse-meal-description) zawsze zwracające bezpieczny fallback zamiast błędu, heurystyczny walidator bezpieczeństwa jako druga warstwa obrony nad strukturalnym ograniczeniem promptu (AI nigdy nie dostaje surowych liczb — tylko zamknięte kody powodów/obserwacji, więc nie ma z czego wymyślić fałszywy związek przyczynowy), dzienny limit kosztu i cache (wyjaśnienia globalnie po kodach, raport tygodniowy raz na dobę). Wpięte w Dzisiaj (wyjaśnienie rekomendacji), Postępy (osobna karta AI pod deterministycznymi obserwacjami, nigdy zmieszana), Przepisy (wyszukiwanie opisowe) i Dziennik (propozycja wartości posiłku z opisu, zawsze do zatwierdzenia). Testy nigdy nie wywołują prawdziwego API —FakeAiProvider. Backend 227/227 i mobile 289/289 testów — patrzdocs/tasks/008-ai-assistant.md. - Task 009 (wdrożenie na Mikrus) — celowo pominięty na tym etapie kolejki zadań; zostanie zrealizowany jako ostatni, po zamknięciu pozostałych zadań funkcjonalnych — patrz
docs/tasks/_009-mikrus-deployment.md. - Task 010 (cele, priorytety i ścieżki aktywności) — zrealizowany w zakresie opisanym w ADR-023: onboarding rozpoczyna się wyborem ścieżki aktywności (spacer, nordic walking, marszobieg, codzienny ruch, bieganie — nigdy trwale zamknięty wybór) zamiast zakładać biegacza, po czym jeden adaptacyjny ekran oceny możliwości pyta tylko o pola adekwatne do wybranej ścieżki (zod
.superRefine()zamiast rozgałęzień schematu, żeby zachować jeden stabilny typ formularza) i ekran celów oferuje katalog celu głównego przefiltrowany per ścieżka plus do trzech celów dodatkowych ze wspólnego katalogu — bez suwaka 0–100, z rangą (główny/ważny/dodatkowy) i deterministycznie wyliczonym miernikiem sukcesu. Przesiew bezpieczeństwa ma teraz cztery wskazówki zamiast dwóch (standardGuidance/conservativeStart/professionalConsultationSuggested/automatedProgressionPaused), wyliczane deterministyczną regułą pierwszeństwa. Profil biegowy sprzed Task 010 (lokalny i w bazie) jest bezstratnie migrowany doActivityPath.runningprzy pierwszym odczycie; pola specyficzne dla biegania są zachowane bez zmiany znaczenia dla kompatybilności wstecznej API. Adaptacja generowania planu do nowych ścieżek pozostaje poza zakresem (Task 011). Backend 229/229 i mobile 322/322 testów, w tym dedykowane testy każdej z pięciu ścieżek aktywności — patrzdocs/tasks/010-activity-goals-onboarding-v2.md. - Task 011 (adaptacyjny silnik priorytetów treningowych i żywieniowych) — zrealizowany w zakresie opisanym w ADR-024: drugi, niezależnie wersjonowany deterministyczny silnik (
PriorityEngine,priority-engine-v1) obokNutritionEngine, oba karmiące jeden wierszDailyPlan— jedyne wspólne źródło priorytetów dla Dzisiaj/Treningu/Żywienia/Postępów. Czternastowartościowa klasyfikacja dnia obejmuje wszystkie pięć ścieżek aktywności (dla biegania redukuje się 1:1 do nietkniętej dotychczasowej klasyfikacji — zero regresji), deterministyczna ocena gotowości (prosty system punktowy, brak danych nigdy nie obniża wyniku) i kaskada propozycji adaptacji z jawną kolejnością (bezpieczeństwo → przeciążenie → gotowość → frekwencja → brama gotowości początkującego → progresja), wymagająca zatwierdzenia użytkownika poza jednym jawnym wyjątkiem bezpieczeństwa. Dziewięć szablonów planu treningowego (był jeden) dobieranych wg ścieżki i poziomu wejściowego, archiwizujących — nie kasujących — niepasujący plan sprzed onboardingu. Postępy zyskały przegląd priorytetów z listą propozycji adaptacji i przyciskami zatwierdź/odrzuć. Podczas implementacji znaleziono i naprawiono rzeczywisty błąd: wielość planów na użytkownika (aktywny + zarchiwizowane) ujawniła brakujące filtrowanie po aktywnym planie w trzech miejscach odpytujących treningi po dacie — zabezpieczone testem regresyjnym. Backend 274/274 i mobile 330/330 testów — patrzdocs/tasks/011-adaptive-training-nutrition-priority-engine.md. - Task 012 (katalog produktów spożywczych) — zrealizowany w zakresie opisanym w ADR-025: katalog produktów podobny do Fitatu (wyszukiwanie po nazwie/marce/kodzie kreskowym, wybór porcji, prywatne produkty, jawny priorytet i jakość źródeł) osadzony jako pięć encji EF (
FoodProduct/FoodBrand/FoodServing/FoodProductRevision/FoodProductFavorite), nie dziesięć nazwanych modeli z dokumentu zadania — reszta to pola/enumy naFoodProduct, tym samym wzorcem coRecipe(Task 005). Jedyny zewnętrzny dostawca to Open Food Facts, wyłącznie dla odczytu po kodzie kreskowym (nigdy wyszukiwania tekstowego), z jawnym priorytetem źródeł przy zapisie — zatwierdzone dane Runth/społeczności nigdy nie są cicho nadpisywane. Moderacja kandydatów to deterministyczne reguły akceptacji (FoodProductValidator), nie kolejka z człowiekiem — dokument zadania to jawnie dopuszcza. Dodawanie produktu do dziennika rozszerza istniejący endpoint i mechanizm offline/outbox (Task 006) o trzy opcjonalne pola zamiast budować drugi mechanizm; serwer zawsze przelicza makra z profilu produktu, nigdy nie ufa wartościom klienta. Backend 309/309 i mobile 340/340 testów — patrzdocs/tasks/012-food-product-catalog.md. - Task 013 (skanowanie kodu kreskowego i etykiety) — zrealizowany w zakresie opisanym w ADR-026:
ILabelRecognitionProviderz dokumentu zadania to istniejącyIAiProvider(Task 008) rozszerzony o opcjonalne zdjęcia, nie osobny port — rozpoznawanie etykiety (nazwa, marka, ilość netto, wartości odżywcze na 100 g/ml, składniki, sugerowane alergeny) zwraca structured output z Claude z jawną kategorią pewności (high/medium/low/unknown) na każde pole, nigdy procentem. Skan kodu kreskowego ponownie wykorzystuje endpoint odczytu po kodzie z Task 012 bez nowego endpointu; skan etykiety prowadzi na to samo, w pełni edytowalne rozszerzenie formularza własnego produktu z Task 012 — z podpowiedzią pewności przy polu, banerem ostrzeżeń niespójności i osobnymi grupami „zawiera”/„może zawierać” dla alergenów, nigdy automatycznym zapisem na podstawie samego OCR. Zdjęcia są przetwarzane wyłącznie w pamięci na czas jednego żądania i nigdy nie trafiają na dysk backendu; aparat i galeria wywoływane są z wyłączonym odczytem EXIF, więc metadane lokalizacyjne nigdy nie powstają. Szkic skanu etykiety żyje wyłącznie w lokalnym magazynie urządzenia (Task 006), celowo poza outboxem — analiza to ponawialne żądanie odczytu, nie kolejkowana mutacja. Backend 319/319 i mobile 355/355 testów — patrzdocs/tasks/013-barcode-label-ocr.md. Kamera i przepływ zdjęć nie zostały fizycznie zweryfikowane na urządzeniu w tym środowisku (brak dostępu do kamery/Expo Development Build) — sprawdzone statycznie (kompilacja,tsc, testy z zamockowanymexpo-camera/expo-image-picker). - Task 014 (analiza zdjęcia posiłku i wspomagane logowanie AI) — zrealizowany w zakresie opisanym w ADR-027:
IMealVisionProviderz dokumentu zadania to ten sam rozszerzonyIAiProviderco Task 013, nie osobna integracja — zdjęcie posiłku (do trzech ujęć) zwraca listę komponentów, każdy z jedną kategorią pewności (nie per pole, jak w Task 013 — dokument zadania wprost tego wymaga), szacowanym zakresem porcji i sugerowanymi niewidocznymi dodatkami (olej, sos...), plus do trzech pytań doprecyzowujących. Dopasowanie komponentu do katalogu dzieje się wyłącznie po stronie mobile przez istniejące wyszukiwanie (Task 012) — endpoint analizy nigdy nie zwraca kandydatów produktu, a energia i makra dla dopasowanego komponentu są zawsze liczone dokładnie tą samą, już przetestowaną ścieżką co ręczne dodanie z katalogu; AI nigdy nie nadpisuje wartości odżywczych. Zapis tworzy jedenFoodJournalEntryna potwierdzony komponent, wszystkie połączone nowymMealPhotoAnalysisId(pełni podwójną rolę znacznika źródła i klucza grupującego — bez nowego polaSource). Zdjęcia nigdy nie trafiają na dysk backendu — jedyna nowa tabela (MealPhotoAnalysis) przechowuje tylko wynik, z hashem obrazu jako kluczem krótkotrwałego cache (identyczne zdjęcie tego samego użytkownika nie jest ponownie wysyłane do AI). Aparat i galeria wywoływane są z wyłączonym odczytem EXIF. Backend 333/333 i mobile 366/366 testów — patrzdocs/tasks/014-ai-meal-photo-analysis.md. Kamera i przepływ zdjęć nie zostały fizycznie zweryfikowane na urządzeniu w tym środowisku (to samo ograniczenie co Task 013) — sprawdzone statycznie (kompilacja,tsc, testy z zamockowanymexpo-camera/expo-image-picker). - Kolejne zadania realizowane po kolei z
docs/tasks— patrz też 16-decisions-and-open-questions.md w razie rozbieżności między mockupami a dokumentacją.
Wymagania: Node.js 24 LTS, .NET SDK 10, Docker Desktop (Compose v2). Szczegóły w 22-local-development.md.
# 1. Sekrety
cp .env.example .env
cp apps/mobile/.env.example apps/mobile/.env
# 2. Backend + baza (Postgres + API w Dockerze)
docker compose up --build -d
curl http://localhost:8080/health/live
curl http://localhost:8080/health/ready
# 3. Aplikacja mobilna
cd apps/mobile
npm install
npm start # otwórz w Expo Go / development buildzieEkran logowania wymaga działającego backendu (EXPO_PUBLIC_API_BASE_URL w apps/mobile/.env, domyślnie http://localhost:8080). Na fizycznym urządzeniu/emulatorze zamiast localhost wpisz adres IP komputera w sieci lokalnej.
Na Windows bez make użyj ./scripts/dev.ps1 <target> (patrz Makefile dla listy targetów: setup, dev-infra, dev-api, dev-mobile, test, lint, format, build, clean).
Weryfikacja jakości mobile (apps/mobile): npm run format:check, npm run lint, npm run typecheck, npm test, npx expo-doctor.
Weryfikacja jakości backendu (root): dotnet format --verify-no-changes, dotnet build, dotnet test (wymaga Dockera — testy Testcontainers).
| Dokument | Cel |
|---|---|
| 00-product-vision.md | wizja, użytkownik i wartość produktu |
| 01-mvp-scope.md | zakres PoC/MVP oraz funkcje odłożone |
| 02-user-flows.md | podstawowe przepływy użytkownika |
| 03-functional-requirements.md | wymagania funkcjonalne |
| 04-non-functional-requirements.md | wydajność, bezpieczeństwo i utrzymanie |
| 05-architecture.md | architektura rozwiązania |
| 06-domain-model.md | model domenowy i encje |
| 07-api-contract.md | kontrakt REST API |
| 08-running-nutrition-engine.md | reguły dopasowania żywienia do biegu |
| 09-strava-integration.md | OAuth, webhooki i synchronizacja Stravy |
| 10-ai-features.md | funkcje AI i ograniczenia bezpieczeństwa |
| 11-security-privacy.md | prywatność i bezpieczeństwo danych |
| 12-infrastructure-mikrus.md | budżetowe wdrożenie na VPS |
| 13-testing-strategy.md | strategia testów |
| 14-roadmap.md | kolejność budowy produktu |
| 15-definition-of-done.md | wspólne kryteria ukończenia |
| 16-decisions-and-open-questions.md | decyzje architektoniczne i pytania |
| 17-ui-ux-spec.md | nawigacja i ekrany mobilne |
| 18-observability-backup.md | logi, monitoring i kopie zapasowe |
| 19-content-data-policy.md | źródła przepisów i danych żywieniowych |
| 20-version-matrix.md | wersje startowe technologii |
| 21-environment-configuration.md | zmienne i środowiska |
| 22-local-development.md | lokalne uruchomienie projektu |
- Agent ma realizować jedno zadanie z
docs/tasksnaraz. - Przed kodowaniem agent musi wskazać dokumenty, na których opiera rozwiązanie.
- Każda zmiana musi zawierać testy lub uzasadnienie ich braku.
- Agent nie może dodawać nowej usługi infrastrukturalnej bez aktualizacji dokumentacji i ADR.
- Agent nie może implementować funkcji spoza MVP „przy okazji”.
- Sekrety nigdy nie trafiają do repozytorium.