Note
To monorepo złożone z submodułów gita. Katalogi frontend/, backend/ i worker/ to odnośniki do osobnych repozytoriów w organizacji HackYeah-TuttiTripTeam. Kliknięcie takiego katalogu na GitHubie przenosi do innego repozytorium, na commit zapisany w tym repozytorium. Wszystkie te repozytoria są publiczne.
Note
Jak powstał kod. TuttiTrip powstał z pomocą asystentów kodowania (Claude Code, Codex). Ludzie z zespołu odpowiadali za architekturę rozwiązania, rozplanowanie funkcji, działanie aplikacji i to, jak się z niej korzysta.
TuttiTrip to planer wyjazdów rodzinnych i grupowych, który układa plan, po którym nikt w grupie nie czuje, że przegrał. Projekt powstaje na hackathonie HackYeah 2026.
To repozytorium zbiera trzy części aplikacji jako submoduły gita: frontend, backend i workera. Kod każdej części żyje w osobnym repozytorium, a tutaj jest opis całości i instrukcja uruchomienia.
W grupie zwykle jedna osoba planuje cały wyjazd. Każdy chce czegoś innego: dziecko park rozrywki, nastolatek coś ze znajomymi, babcia muzeum i odpoczynek, partner tanio i blisko. Plany z czatbotów brzmią dobrze, ale często pomijają godziny otwarcia, odległości, tempo najwolniejszej osoby i wymagania co do noclegu.
- Organizator opisuje wyjazd jednym zdaniem („Gdańsk, trzy dni, dzieci 6 i 13 lat, babcia”), a asystent dopytuje tylko o to, co najbardziej zmienia plan. Odpowiedzi zbiera kartami i suwakami. Pozostałych uczestników organizator dodaje jako profile, więc nie muszą zakładać kont.
- Dla każdego miejsca każda osoba ma „chcę”, „nie chcę” albo „obojętnie”, a przy „nie chcę” podaje powód. Aplikacja pokazuje werdykt z uzasadnieniem. Organizator może go nadpisać i widzi, ile to kosztuje (sprawiedliwość, budżet, czas).
- Plan układa deterministyczny solver, który dzieli zadowolenie między osoby możliwie po równo, z uwzględnieniem wag i budżetu grupy.
- Linter planu sprawdza godziny otwarcia, dystanse, tempo, przerwy, budżet i wymagania noclegowe. Działa też na planie z innego narzędzia, np. wklejonym z czatbota.
- Wymagania wobec noclegu (basen, kuchnia, parking, konkretna platforma) są kontraktem sprawdzanym dla każdej nocy.
- Wydatki zasilają budżet planu i rozliczenie. W trakcie wyjazdu plan można przeliczyć, gdy pada deszcz albo dziecko jest zmęczone.
Nowe jest to, że model językowy prowadzi rozmowę i pisze uzasadnienia, ale o planie decyduje zwykły kod (solver, linter, reguły cenowe, rozliczenie). Te same dane wejściowe dają ten sam plan, a liczbę naruszeń reguł da się policzyć i porównać z planem z czatbota. Testy architektury w backendzie pilnują, żeby ta logika nie zależała od frameworka webowego, agentów AI ani bazy.
Plansze to makiety zbudowane na tokenach, krojach, ikonach i logo z naszego design systemu, na danych przykładowych (rodzina w Gdańsku). Odwzorowują frontend z gałęzi develop. Miara sprawiedliwości (wagi osób, suwak α, ocena i weto) jest już w aplikacji, ale jej ekran wygląda inaczej niż na planszach. Przeplanowanie po nagłym zdarzeniu (plansza z deszczem) jest w budowie i nie ma jeszcze ekranu. Zrzuty z działającej aplikacji są niżej.
Prawdziwy frontend z gałęzi develop na danych testowych (MSW). Zmieniliśmy tylko nazwę konta testowego i adres źródła cen. Zrzuty z laptopa przełączają się na ciemny motyw razem z GitHubem. Więcej ekranów jest w README frontendu.
Wywiad po pierwszym zdaniu: karta budżetu i panel „Co już wiem”. |
Plan ze skrótem wersji, kosztem dla grupy i znacznikami weryfikacji cen i godzin. |
Rozliczenie: saldo każdej osoby i najmniejsza liczba przelewów. |
Głosowanie z linku dla osoby bez konta, z wetem. |
Na telefonie: wyjazd zakładany głosem, zgoda na przekroczenie budżetu i głosowanie z linku bez konta.
![]() |
![]() |
![]() |
| Środowisko | Aplikacja | API (Swagger) |
|---|---|---|
produkcja (main) |
https://tuttitrip.gburek.app | https://tuttitrip-api.gburek.app/api/v1/docs |
| develop | https://tuttitrip-develop.gburek.app | https://tuttitrip-api-develop.gburek.app/api/v1/docs |
Wszystkie endpointy backendu są pod /api/v1/. Ten sam adres działa też przez domenę aplikacji, np. https://tuttitrip.gburek.app/api/v1/docs.
Aplikacja to PWA, więc na telefonie można ją dodać do ekranu głównego.
Dane z TuttiTrip można podłączyć do Claude, ChatGPT i Claude Code przez serwer MCP (https://tuttitrip-api.gburek.app/api/v1/mcp). Instrukcja krok po kroku, wymagane plany i co widać po zalogowaniu: Serwer MCP w README backendu.
Prezentacje z docs/presentations są na https://tuttitrip-decks.gburek.app (lista pod /, np. https://tuttitrip-decks.gburek.app/TuttiTrip_pitch_deck). Każda inna gałąź tego repozytorium dostaje własny adres https://tuttitrip-decks-<gałąź>.gburek.app.
| Katalog | Repozytorium | Co zawiera |
|---|---|---|
frontend/ |
tuttitrip-frontend |
aplikacja webowa (PWA) |
backend/ |
tuttitrip-backend |
API, baza danych, solver i linter |
worker/ |
tuttitrip-worker |
zadania w tle: agenci AI, embeddingi, przeliczenia |
Wszystkie trzy repozytoria są publiczne, więc git clone --recurse-submodules pobierze je bez dostępu do organizacji.
To samo w wersji, którą GitHub rysuje z kodu:
flowchart LR
user["Organizator<br/>(telefon lub przeglądarka)"]
fe["frontend<br/>React + Vite, PWA<br/>Cloudflare Workers"]
be["backend<br/>FastAPI<br/>S1 szybko decyduje, S2 (LLM) pisze, S3 czeka na zgodę"]
db[("PostgreSQL 18<br/>+ pgvector<br/>+ kolejki DBOS")]
wk["worker<br/>DBOS + Pydantic AI"]
gb["Modele na GB10<br/>Qwen, basal, Laya"]
openr["OpenRouter<br/>zapas modeli"]
rt["OpenAI Realtime<br/>mowa"]
gm["Google Maps<br/>mapa i karta miejsca"]
auth["Auth0"]
user --> fe
fe -- "REST (typy z OpenAPI)" --> be
fe -. logowanie .-> auth
fe -. "audio (WebRTC)" .-> rt
fe -. mapa .-> gm
be -. "sideband mowy" .-> rt
be -- "dane, zlecanie zadań" --> db
be --> gb
wk -- "pobiera zadania, zapisuje wyniki" --> db
wk --> gb
wk -. zapas .-> openr
Szczegółowy diagram, decyzje D1 do D7 z powodami, mapa modeli, warunki Google Maps i słownik są w docs/architektura.md.
- Frontend (tuttitrip-frontend): React 19, TypeScript, Vite, TanStack Router i Query, shadcn/ui na Tailwind v4. Działa jako PWA na Cloudflare Workers. Typy zapytań generuje z
/api/v1/openapi.jsonbackendu, więc niezgodność z API wychodzi już przy kompilacji. API woła pod własną domeną (/api/v1/...), a Worker przekazuje te zapytania do backendu swojego środowiska. - Backend (tuttitrip-backend): Python 3.14, FastAPI, Pydantic, SQLAlchemy i Alembic, PostgreSQL 18 z pgvector, logowanie przez Auth0. Domeny (wyjazdy, profile, wywiad, planowanie, noclegi, wydatki) są osobnymi modułami. Solver sprawiedliwości, linter i rozliczenie to czysta logika bez zależności od frameworka. Długich operacji backend nie wykonuje sam, tylko zleca je workerowi przez kolejkę DBOS i odczytuje ich stan.
- Worker (tuttitrip-worker): trwałe workflowy DBOS z agentami Pydantic AI. Postęp zapisuje w Postgresie, więc po restarcie kończy zadanie od ostatniego kroku i nie powtarza udanych wywołań modelu. Modele bierze z jednego katalogu Pydantic AI: Qwen3.8-27B i modele decyzyjne (basal, Laya) działają na GB10, a OpenRouter jest zapasem. Embeddingi liczy lokalnie (Ollama,
nomic-embed-text). Backend i worker łączy wersjonowany kontrakt, który CI sprawdza w obu repozytoriach.
git clone --recurse-submodules https://github.com/HackYeah-TuttiTripTeam/tuttitrip.git
cd tuttitripJeśli repozytorium jest już sklonowane bez submodułów albo chcesz pobrać najnowsze main każdej części:
git submodule update --init --remoteSubmoduły śledzą gałąź main. W tym repozytorium zapisany jest konkretny commit każdej części. Żeby przesunąć go na aktualne main:
git submodule update --remote
git add frontend backend worker
git commit -m "Aktualizacja submodułów"
git pushDo pracy nad kodem lepiej klonować repozytoria części osobno. Każde ma własne CI, gałąź develop i podglądy dla gałęzi.
Wymagania:
- Docker z Docker Compose (lokalny Postgres z pgvector),
- uv dla backendu i workera (sam pobierze Pythona 3.14),
- Node.js 22+ i pnpm (przez
corepack) dla frontendu, - opcjonalnie: klucz OpenRouter albo lokalny model zgodny z API OpenAI dla agentów oraz Ollama z
nomic-embed-textdla embeddingów. Testy nie potrzebują żadnego z nich.
Backend startuje pierwszy, bo do niego należą schemat bazy, migracje i tabele DBOS.
API wystartuje na http://localhost:8000.
cd backend
uv sync
cp .env.example .env
docker compose up -d --wait db
uv run alembic upgrade head
uv run dbos migrate -s postgresql://tuttitrip:tuttitrip@localhost:5432/tuttitrip
uv run uvicorn tuttitrip.main:app --reloadW drugim terminalu:
cd worker
uv sync
cp .env.example .env # domyślne wartości pasują do bazy z compose backendu
uv run tuttitrip-workerCzy backend i worker się widzą, sprawdzisz tak:
curl -X POST localhost:8000/api/v1/jobs/ping # zwraca workflow_id
curl localhost:8000/api/v1/jobs/ping/<workflow_id> # status SUCCESSW trzecim terminalu. Aplikacja wystartuje na http://localhost:5173.
cd frontend
corepack enable
pnpm install
cp .env.example .env.local
pnpm api:sync # typy API z lokalnego backendu
pnpm devBez zmiennych VITE_AUTH0_* aplikacja działa z wyłączonym logowaniem. Frontend można też podłączyć do wdrożonego API: VITE_API_URL=https://tuttitrip-api-develop.gburek.app.
Szczegóły (zmienne środowiskowe, port bazy inny niż 5432, uruchamianie w kontenerach, testy i lint, zasady architektury) są w README każdej części:
Zadania prowadzimy w projekcie GitHub organizacji. Pracę nad zadaniem zaczynamy na gałęzi feature/... od develop i otwieramy PR do develop. PR do develop mergujemy przez "Squash and merge", a wydanie to PR z develop do main mergowany przez "Create a merge commit". Każda gałąź dostaje własny podgląd frontendu i API.
Zgłoszenia i PR mają stały format. Tytuł zgłoszenia zaczyna się od feat:, docs:, chore: albo bug: (np. feat(frontend): Filtrowanie wyjazdów po dacie), a treść ma sekcje Opis, Dlaczego, Kryteria akceptacji, Definition of Done i Obszar. Zgłoszenie błędu opisuje dodatkowo kroki do odtworzenia, oczekiwane i faktyczne zachowanie oraz środowisko. Formularz New issue prowadzi przez te pola, a zgłoszenia w innym formacie bot zamyka z komentarzem, co poprawić. Tytuł PR ma te same prefiksy, tylko poprawka to bugfix:, a wydanie release:. Opis PR piszemy po polsku według szablonu. Notatki wydań powstają same w GitHub Releases. Szczegóły są w CONTRIBUTING.md.
Na kanał zespołu na Discordzie trafiają:
- wyniki workflow z czterech repozytoriów: CI, wdrożenia (z adresem: produkcja,
develop, podgląd PR, API gałęzi), sprzątanie i sprawdzenia formatu. Wysyła je.github/workflows/discord-notify.ymlw każdym repozytorium przez wspólny workflow z repozytorium.github, - zmiany w projekcie GitHub (nowy element, zmiana Status, Area, Priority, archiwizacja, usunięcie) oraz nowe, zamknięte i scalone issue i PR. Wysyła je Worker Cloudflare
tuttitrip-discord-relay(https://tuttitrip-hooks.gburek.app) z webhooka organizacji.
Żeby kanał nie zarastał: uruchomienia skipped nie są wysyłane, sprawdzenia formatu, usuwanie gałęzi, notatki wydań i sprzątanie piszą tylko przy błędzie, a sukces na gałęzi roboczej i anulowanie to jedna linia. Pełną wiadomość dostają sukcesy na main i develop oraz każdy błąd.
Webhook Discorda jest sekretem DISCORD_WEBHOOK_URL w każdym repozytorium i w Workerze. Konfiguracja, rotacja webhooka i szczegóły zasad: CONTRIBUTING.md, "Powiadomienia (Discord)".
| Imię i nazwisko | Rola | Obszary |
|---|---|---|
| Łukasz Gęborys | Lider zespołu | AI i Data Science, cyberbezpieczeństwo, zarządzanie projektem i produktem |
| Cyprian Gburek | Uczestnik | AI i Data Science, Backend Developer, bazy danych, inżynieria danych, DevOps i chmura, Frontend Developer, prezentacje i wystąpienia publiczne, zarządzanie projektem i produktem, architektura oprogramowania, wytwarzanie oprogramowania, aplikacje webowe i mobilne |
| Angelika Korcz | Uczestniczka | cyberbezpieczeństwo |
| Michał Sadrzak | Uczestnik | AI i Data Science, UI, aplikacje webowe i mobilne, wytwarzanie oprogramowania |
| Tomasz Florczak | Uczestnik | inne technologie |












