# Do zrobienia

Lista otwarta po scaleniu gałęzi `mecze-wieloformatowe` (2026-08-23).

## Konwencja: polskie znaki w tekstach dla użytkownika

Od gałęzi `polskie-znaki` (2026-08-23) każdy tekst, który użytkownik widzi w
przeglądarce albo w terminalu — szablony, etykiety i komunikaty formularzy,
komunikaty `addFlash`/`FormError`, wyjątki pokazywane na ekranie, etykiety
wyliczeń (`MatchOutcome`, `MatchFormat`), teksty komend konsolowych — pisze się
pełną polszczyzną, ze wszystkimi znakami diakrytycznymi. To nie jest już zasada
projektowa „bez ogonków" — została świadomie wycofana na życzenie właściciela.

Komentarze w kodzie (`//`, `/* */`, docbloki) zostają bez znaków diakrytycznych,
tak jak dotychczas — to nie jest tekst dla użytkownika, tylko konwencja zapisu
kodu, i nie warto robić w niej wstecznego diffu bez wartości. Nazwy klas, metod,
zmiennych, tras, kluczy tablic i identyfikatorów HTML/CSS też zostają bez zmian.

## Konwencja: JavaScript wyłącznie do podpowiadania nazw drużyn

Od gałęzi `podpowiadanie` (2026-08-30) zasada „zero JavaScriptu" nie obowiązuje
już w całości: zero budowania frontendu zostaje w mocy (żadnych bibliotek,
żadnego kroku budowania, żadnych zasobów z sieci zewnętrznej), ale jeden plik,
`public/js/team-autocomplete.js`, wczytywany zwykłym znacznikiem `<script>`
w `templates/base.html.twig`, to świadome i wąskie odstępstwo — zamówione przez
właściciela, wyłącznie do podpowiadania nazw drużyn w polach oznaczonych
atrybutem `data-entry-autocomplete` (na `/team`, `/impersonate` i w formularzu
`/fixture/new`). Zasila go punkt końcowy `App\Controller\EntrySuggestionController`
(`GET /entries/search`), oparty wprost na
`App\Repository\FplEntryRepository::searchByNameFragment`. Formularze działają
normalnie także bez JavaScriptu — wyłącznie bez podpowiedzi, numer drużyny da
się wtedy wpisać wprost tak jak dotychczas.

## Zamówione, nierozpoczęte

**Rozmiar strony i sposób liczenia jako dwa niezależne wybory.**
Dziś „format" niesie naraz dwie informacje: ile drużyn gra po stronie i jak liczy
się wynik. Dlatego duety mają na sztywno przyklejone liczenie sumą, a trójki
pojedynkami — i nie da się wyrazić meczu 2 na 2 rozstrzyganego pojedynkami.
Rozdzielić na: liczba drużyn (1–4) i sposób liczenia (suma / pojedynki / miejsca).
Liczenie sumą i pojedynkami to już osobne, przetestowane klasy; trzeba odkleić
`DuelsScorer` od założenia, że pojedynki są dokładnie trzy, i podmienić jedno pole
w bazie na dwa. Miejsca zostają zarezerwowane dla ósemki.

**„No to cyk" (81877) wpisane na stałe jako moja drużyna.**
Właściciel gra zawsze tym samym zespołem, więc formularz nie ma o niego pytać.
Wypełnia pierwszą pozycję mojej strony we wszystkich formatach doraźnych; przy
pojedynku moja strona to wyłącznie ona i pole w ogóle nie musi się pojawiać.
Decyzja właściciela: na stałe w kodzie, bez osobnego ekranu ustawień.

## Znalezione przez recenzję, odłożone

**Strona kolejki pobiera dane live raz na mecz, nie raz na kolejkę.**
`GameweekController` woła `MatchViewBuilder::build()` w pętli, a każde wywołanie
robi własne `syncMany()`, które bezwarunkowo pobiera `event/{gw}/live/`. Kolejka
z czterema meczami to cztery pobrania tego samego, kilkumegabajtowego JSON-a przy
każdym tyknięciu crona w trakcie trwającej kolejki. Zapamiętanie `LiveDto` na czas
żądania to kilka linijek. Kolejki zamrożone nie generują ruchu w ogóle.

**Numer pojedynku na ekranie bierze się z pozycji w tablicy, nie z `Duel::getSlot()`.**
Dziś zgodne, bo kolekcja ma `OrderBy(slot ASC)`, ale to milczące założenie.
Poprawka to jedno pole w `DuelPair`.

**Gałąź „stała czwórka niekompletna" jest nieosiągalna.**
`SquadManager::saveMySquad()` zawsze zapisuje dokładnie cztery pozycje, więc
`!isComplete(4)` nigdy nie padnie — realnie działa tylko sprawdzenie na `null`.
Do uproszczenia.

**Testy tworzą składy meczów ze znacznikiem `mine = true`.**
Wbrew ustalonej semantyce: `mine` oznacza wyłącznie stałą czwórkę z ustawień.
Nic nie psuje, bo te testy nie wołają `findMine()`, ale utrwala zły nawyk.

**Profiler Symfony ma wyciszone zgłoszenie bezpieczeństwa.**
`composer.json` wycisza CVE-2026-45072 (XSS w profilerze), bo łatka jest dopiero
w Symfony 7.4, a projekt stoi na 7.3. Profiler jest zależnością wyłącznie
deweloperską i wdrożenie instaluje pakiety z pominięciem dev, więc na serwer nie
trafia. Przy podniesieniu Symfony do 7.4 wyciszenie należy usunąć.

**Statystyki wejść rosną bez końca.**
`page_visit` nie ma żadnego sprzątania: jeden wiersz na odsłonę, w tym wejścia
botów, które przy publicznym adresie potrafią być większością ruchu. Przy tej
skali to kilkaset wierszy miesięcznie i przez długi czas nic się nie stanie, ale
docelowo przydaje się kasowanie wierszy starszych niż rok — najlepiej przy okazji
istniejącego pingu crona, bez osobnego zadania.

**Przypisanie wejść po IP myli osoby dzielące łącze.**
Kto pierwszy wskaże swoją drużynę na `/team`, ten przejmuje wszystkie wcześniejsze
nieprzypisane wejścia z tego adresu — także cudze. Świadoma zgoda właściciela:
alternatywą jest osobne ciasteczko, czyli drugi mechanizm tożsamości obok numeru
drużyny w sesji.
