diff --git a/.claude/settings.json b/.claude/settings.json index 036e1e8..18a6db1 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -27,7 +27,13 @@ "Bash(curl -s -o /dev/null -w \"https://teecup.teeoff.no/dashboard -> %{http_code}\\\\n\" https://teecup.teeoff.no/dashboard)", "Bash(curl -s -o /dev/null -w \"https://teecup.teeoff.no/my-notifications -> %{http_code}\\\\n\" https://teecup.teeoff.no/my-notifications)", "Bash(curl -s -o /dev/null -w \"https://teeoff.no/ -> %{http_code}\\\\n\" https://teeoff.no/)", - "Bash(curl -s https://teecup.teeoff.no/notifications)" + "Bash(curl -s https://teecup.teeoff.no/notifications)", + "Bash(grep -n \"God dag, {me.display_name}\" /opt/teecup/frontend/components/dashboard.tsx)", + "Bash(grep -n \"@router\\\\.\\\\\\(post\\\\|get\\\\\\)\\(\\\\s*$\\\\|@router\\\\.\\\\\\(post\\\\|get\\\\\\)\\(\\\\\"\" /opt/teecup/app/routers/tournaments.py)", + "Bash(python3 test_holeless_course_crash.py)", + "Bash(sed -i 's/check\\(\"normal course: hole-score submit still succeeds \\(regression\\)\", status == 200\\)/check\\(\"normal course: hole-score submit still succeeds \\(regression\\)\", status == 201\\)/' test_holeless_course_crash.py)", + "Bash(sed -i 's/if status != 200:/if status != 201:/' test_holeless_course_crash.py)", + "Bash(sed -i 's/check\\(\"normal course: second side'\"'\"'s hole-score submit succeeds\", status == 200\\)/check\\(\"normal course: second side'\"'\"'s hole-score submit succeeds\", status == 201\\)/' test_holeless_course_crash.py)" ] } } diff --git a/ARCHITECTURE_DECISIONS.md b/ARCHITECTURE_DECISIONS.md index 37f4ddd..513f86b 100644 --- a/ARCHITECTURE_DECISIONS.md +++ b/ARCHITECTURE_DECISIONS.md @@ -233,6 +233,17 @@ ikke to lag i det hele tatt) passer IKKE inn i denne ADR-ens to-lags-modell — en fremtidig, egen ADR trengs når/hvis dette tas fatt på, samme mønster som knockout-punktet over. Kun notert her ennå, ikke designet eller bygget. +**Merk (2026-07-26), presisert og utvidet:** brukeren bekreftet at ønsket +er STØRRE enn Københavner som ett format blant flere — TeeCup skal etter +hvert støtte ekte INDIVIDUELLE turneringer (helt uten lag) som eget +generelt tilfelle, disse skal kunne gå over FLERE RUNDER, og det skal +være mulig å sette opp et Order of Merit (sesong-sammenlagt på tvers av +flere separate arrangementer). Full analyse, inkl. en reell strukturell +kollisjon med ADR-033 sitt bevisst org-uavhengige rundesystem, i +FEATURE_BACKLOG.md ("Utvidelse 2026-07-26: individuelle turneringer, +flerrunde-turneringer, og Order of Merit"). Ren notat-runde, ingen ADR +skrevet ennå. + --- ## ADR-012 — To scoring-moduser per økt @@ -3060,6 +3071,156 @@ FEATURE_BACKLOG.md for full detalj. --- +## ADR-037: Individuelle turneringer, flerrunde-turneringer — grunnstruktur + +**Kontekst:** reist 2026-07-26, som følge av en avklaringsrunde om et +ønsket turnering-leaderboard (se FEATURE_BACKLOG.md sin "Utvidelse +2026-07-26"-seksjon for hele forhistorien). TeeCup skal etter hvert +kunne arrangere turneringer for INDIVIDUELLE spillere (f.eks. +«Københavner» — se FEATURE_BACKLOG.md sitt eget punkt om flere +turneringsformater), ikke bare dagens Ryder Cup-lagformat (ADR-011), +og disse skal kunne gå over FLERE RUNDER med sammenlagt resultat. Et +fremtidig Order of Merit (sesong-sammenlagt på tvers av flere +turneringer, brukerens eksempel: "klubbdager") er identifisert som et +beslektet, men separat, SISTE steg — se eget avsnitt nederst, ikke +designet i denne runden. + +Denne ADR-en dekker KUN grunnstrukturen (datamodell-plassering, +turnering-type, flerrunde-støtte, poengmodell). Selve de fem konkrete +formatene (Københavner, High-low-high, Robbins, Try all, +Flaggturnering) fra FEATURE_BACKLOG.md designes hver for seg OVENPÅ +denne grunnstrukturen, ikke i denne runden. + +**Viktig presisering underveis, som endrer en tidligere antakelse +(2026-07-25-runden om "flere flighter i én frittstående runde"):** i en +formell, org-arrangert individuell turnering er "flight" KUN en +tee-tid-/spilletempo-gruppering (som i dag for lag-turneringer) — IKKE +en leaderboard-grense. Leaderboardet spenner alltid HELE feltet, +uavhengig av hvem som spilte sammen. Dette er strukturelt ulikt den ad +hoc "flere flighter i en frittstående runde"-ideen (der leaderboardet +bevisst skal avgrenses til det man selv satte opp) — de to +"flight"-begrepene ligner i UI, men er IKKE samme konsept. Holdes +bevisst adskilt, ikke forent, selv om forrige runde antydet det motsatte. + +### Beslutning A — Ny, parallell org-scopet datamodell (IKKE gjenbruk av `round`) + +En individuell/flerrunde-turnering får sin EGEN, RLS-beskyttede +tabellstruktur under `organization_id` — IKKE en utvidelse av de +frittstående rundetabellene (`round`/`round_participant`/`round_hole`, +ADR-033). + +**Begrunnelse:** ADR-033 Beslutning A var et BEVISST valg om at +frittstående runder er 100 % org-uavhengige (`plain_connection()`, +ingen RLS i det hele tatt — autorisasjon håndheves med +`WHERE owner_user_id = $1` i app-laget). Å gi `round` en valgfri +`organization_id`/`tournament_id` ville krevd HYBRID RLS (håndhevet kun +når organisasjon er satt) — en helt ny klasse sikkerhetslogikk som +IKKE finnes noe sted ellers i systemet i dag (`organization_id på ALLE +domenetabeller, håndhevet av RLS` er en ufravikelig invariant i +CLAUDE.md — en betinget/nullbar variant ville vært det FØRSTE +unntaket). Dette prosjektet har allerede én dokumentert hendelse +(RLS-tomstreng-bugen, se CLAUDE.md-status 2026-07-16) som kom av +nettopp denne typen RLS-finesse — vi unngår bevisst å introdusere en +ny variant av samme risikoklasse. Noe skjema dupliseres (hull-for-hull- +registrering ligner mye på `round_hole`), men den delte regnelogikken +(`handicap_engine.py`) er allerede rammeverk-uavhengig og gjenbrukes +uendret uansett hvilken tabell dataene ligger i. + +### Beslutning B — Samme `tournament`-tabell, ny type-diskriminator + +`tournament` får en ny kolonne, f.eks. `format_type` (`'team'` | +`'individual'`, default `'team'` for bakoverkompatibilitet med alle +eksisterende rader). + +**Begrunnelse:** en individuell turnering trenger fortsatt navn, status, +datoer, synlighet (ADR-018), invitasjonskode (ADR-020), org-eierskap — +ALT dette er allerede bygget og fungerer uendret på `tournament`-raden, +uavhengig av format. Å lage en helt ny toppnivå-entitet ville betydd å +gjenoppbygge synlighet/join-kode/landingssider fra bunnen for et andre +system — ren duplisering uten reell gevinst. Lag-tabellene +(`team`/`team_roster`) blir ganske enkelt ubrukte for +`format_type = 'individual'` — håndheves i app-laget (samme mønster som +ADR-011s to-lags-grense), ikke med en tung DB-constraint på tvers av +tabeller. + +### Beslutning C — Flerrunde via en ny, økt-lignende tabell + +Individuelle turneringer kan ha FLERE runder fra start, via en ny +`tournament_round`-tabell (org+tournament-scopet, samme rolle som +`session` har for lag-turneringer i dag — dato/bane/hullomfang per +runde). Sammenlagt resultat på tvers av rundene i turneringen summeres +ved lesing, SAMME "summer på ekte deltaker-id, aldri på en løs +side-label"-prinsipp som det eksisterende lag-leaderboardet allerede +bruker (`fetch_leaderboard`, `app/routers/tournaments.py`) for å unngå +å blande sammen feil rader. + +**Bane-referanse:** `tournament_round.course_id` peker til den +EKSISTERENDE org-scopede `course`-tabellen (samme som `session` bruker +i dag) — INGEN snapshot-mekanisme som i frittstående runder +(ADR-033s `course_name_snapshot`). Organisasjonens egen banedata er +allerede stabil, admin-kontrollert data; snapshot-behovet i ADR-033 kom +av at frittstående runder leser LIVE fra en ekstern, ikke-org-kontrollert +kilde (teeoff) — samme begrunnelse gjelder ikke her. + +**Deltaker-identitet:** en ny `tournament_participant`-tabell (org+ +turnering-scopet, samme rolle som `team_roster` — refererer til +EKSISTERENDE `player`-tabellen, fryser `handicap_index_snapshot` ved +uttak, samme reproduserbarhetsprinsipp som ADR-007) — IKKE en ny, +frittstående gjeste-modell slik `round_participant` har. En turnering +har alltid en organisator som administrerer en kjent spillerpool +(samme som lag-turneringer i dag), ulikt en privatpersons frittstående +runde. + +### Beslutning D — Rå slag lagres, poeng caches per format (samme mønster som match-play) + +Hver `tournament_round`-deltaker sin hull-for-hull-registrering lagrer +RÅ BRUTTO SLAGTALL per hull (samme grunnform som `round_hole.score`) — +scorekortet trenger dette uansett, og det gir handlefrihet til å vise +flere ulike visninger (brutto/netto/stableford/Københavner-poeng) av +SAMME underliggende data. + +I TILLEGG caches et FERDIG UTREGNET poengtall per format — samme +etablerte mønster som `match.status_text`/`points_side_a/b` i dag: +motoren (`handicap_engine.py`, ny funksjon per scoringsmetode) regner +poeng ved HVER hull-innsending, resultatet lagres i en egen kolonne +(f.eks. på en ny `tournament_round_score`-rad, én per deltaker per +runde) — rask leaderboard-lesing uten å måtte regne ut alt på nytt for +hele feltet ved hver visning, konsistent med hvordan matchstatus +allerede caches og regnes på nytt ved hver innsending +(`recompute_and_cache_match_state`). + +`tournament.scoring_method` (eller ev. per `tournament_round`, hvis et +fremtidig format trenger å blande metoder — ikke avklart, default: fast +per turnering) avgjør HVILKEN motorfunksjon som brukes til å regne +poeng fra de rå slagene — ny CHECK-verdi + ny ren funksjon i +`handicap_engine.py` per format som legges til (`stroke_play_gross`, +`stroke_play_net`, `stableford`, `copenhagen_points`, ...). Hvert nytt +format fra FEATURE_BACKLOG.md sin liste blir dermed i hovedsak: én ny +motorfunksjon (testet isolert, samme "test i isolasjon FØR resten"- +prinsipp som ADR-005) + én ny CHECK-verdi, ikke en skjemaendring. + +### Konsekvens — hva denne ADR-en IKKE avgjør ennå + +- **De fem konkrete formatene** (Københavner, High-low-high, Robbins, + Try all, Flaggturnering) — hver trenger sin egen, mindre design-runde + oppå denne grunnstrukturen (poengformel, evt. spesielle + paringsregler for High-low-high/Robbins som IKKE er rent individuelle + poeng-per-hull). +- **Order of Merit** (sesong-sammenlagt på tvers av FLERE separate + turneringer). Bekreftet av brukeren som et beslektet, men SISTE steg + — forutsetter at individuelle turneringer med et poengresultat + finnes først. Krever sannsynligvis et helt nytt overordnet konsept + (en "sesong"/"serie", org-scopet, RLS som ellers) som grupperer flere + `tournament`-rader og akkumulerer poeng — IKKE designet i denne + runden. +- **Migrasjon/kode** — denne ADR-en er ren struktur-beslutning, ingen + migrasjon er skrevet ennå. Neste steg er å legge frem et konkret + migrasjonsutkast (nye tabeller + `tournament.format_type`-kolonne) til + gjennomgang før noe kjøres, samme "vis planen FØR noe skrives"-mønster + som ellers i prosjektet. + +--- + ## Åpne spørsmål (ikke besluttet ennå) Disse må avklares før eller under de relevante fasene: @@ -3080,6 +3241,17 @@ Disse må avklares før eller under de relevante fasene: 5. **Individuell-vs-delt-ball i `hole_score`:** Håndheves i app-laget, ikke av databasen (CHECK når ikke opp til `session.format`). Motoren/API-et må passe på at f.eks. et foursome ikke får per-spiller-scorer. +6. **Individuelle turneringer, flerrunde-turneringer og Order of Merit** + (reist 2026-07-26). **Grunnstruktur for de to første AVKLART samme + dag, se ADR-037:** ny, parallell org-scopet datamodell (IKKE en + utvidelse av ADR-033s `round`-tabeller), samme `tournament`-tabell + med ny `format_type`-diskriminator, flerrunde via ny + `tournament_round`-tabell, rå slag lagres + poeng caches per format + (samme mønster som match-play). **Fortsatt åpent:** de fem konkrete + formatenes egne poengregler (Københavner m.fl.), og Order of Merit + (sesong-sammenlagt på tvers av flere turneringer) — bekreftet som et + beslektet, men SISTE steg, ikke designet ennå. Ingen migrasjon + skrevet — ADR-037 er ren struktur-beslutning. --- diff --git a/CLAUDE.md b/CLAUDE.md index 81cae85..75dc6fb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -62,6 +62,24 @@ Les dette først i hver økt. Det koder hva vi har bestemt og hvordan vi jobber. annen runde — ingen egen stor retrofit-runde er igangsatt eller bedt om ennå. +## Navneformat (ufravikelig, gjelder ALT, eksisterende og fremtidig) +- Brukeren instruerte eksplisitt 2026-07-26: når TeeCup kommuniserer + DIREKTE TIL brukeren (adresserer dem, "snakker til" dem) — f.eks. en + dashbord-hilsen ("God morgen Erol") eller en e-post/varsel rettet til + mottakeren selv — skal KUN FORNAVN brukes, aldri fullt navn. +- Til vanlig (lister, roster, chat-forfatter, "fjern X"-bekreftelser, + administrasjonsvisninger, alt som IKKE er direkte adressering) brukes + fortsatt fullt navn, eller initial(er)+etternavn, eller annet som er + nødvendig for å skille personer fra hverandre — ikke fornavn alene der. +- Samme STÅENDE forventning som tilgjengelighetsregelen over: gjelder + fremtidig arbeid direkte, og rettes opportunistisk når en skjerm + likevel røres — ingen egen retrofit-runde igangsatt. +- **✅ FIKSET 2026-07-26** (samme dag, i "fiks alle kjente små bugs"- + runden): dashbordets hilsen brukte fullt navn — rettet til + `me.first_name ?? me.display_name` (`/auth/me` eksponerte allerede + `first_name` separat, ingen backend-endring nødvendig). Se + CLAUDE.md-status lenger ned for full detalj. + ## Arbeidsmåte - Inkrementelt. Ingenting tas for gitt før det er testet. Bekreft hvert steg før du går videre. @@ -2855,6 +2873,115 @@ Ferdig og verifisert: FastAPI (ikke bare at Next.js svarte): anonymt `GET /notifications` over ekte https ga korrekt `401 NOT_AUTHENTICATED`, ikke en rå 404. +- **Navneformat-regel + turnering-scope avklart, IKKE bygget (2026-07-26):** + brukeren instruerte at direkte adressering (hilsener, e-post/varsler + rettet til mottakeren) alltid skal bruke KUN fornavn, mens vanlige + visninger (lister, roster, chat) fortsatt bruker fullt navn/initialer — + lagt til som ny stående regel (se egen seksjon over). Kjent brudd + notert: dashbord-hilsenen bruker i dag fullt navn, ikke rettet ennå. + Deretter bedt om analyse av .md-filene for prioritering — landet på å + avklare turnering-leaderboard-spørsmålet fra 2026-07-25 først. Svaret + avdekket et vesentlig STØRRE ønske enn antatt: TeeCup skal etter hvert + støtte ekte INDIVIDUELLE turneringer (ikke bare lag), disse skal kunne + gå over flere runder, og det skal være mulig med et Order of Merit + (sesong-sammenlagt på tvers av flere arrangementer, brukerens eksempel: + "klubbdager" gjennom en sesong). Grundig notert i FEATURE_BACKLOG.md og + ARCHITECTURE_DECISIONS.md (ADR-011s eget notat + ny "Åpne spørsmål"- + post 6) — inkl. en reell strukturell kollisjon identifisert: en + flerrunde-individuell-turnering vil kollidere med ADR-033 sitt bevisst + org-UAVHENGIGE frittstående rundesystem, må avklares eksplisitt før + bygging. **Ren notat-runde, ingen ADR skrevet, ingen kode** — brukeren + ba eksplisitt kun om at dette dokumenteres nå. +- **Flere flighter i én frittstående runde — presisert videre, fortsatt + IKKE besluttet (2026-07-26):** oppfølgende avklaring samme runde. + Brukeren presiserte: oppsett av flere flighter er ÉN handling utført av + én person (samme gjest-mønster som i dag, bare flere flight-grupper), + scoreregistrering per flight er et SENERE, separat ansvar (forventet + løst av ekte medspillere, ADR-036 fase 3) — og viktigst: leaderboardet + skal KUN dekke flightene som ble satt opp SAMMEN i én handling, ikke + "alle som spilte samme bane samme dag". Det siste peker sterkt mot + retning 1 (løs gruppering av separate `round`-rader) fra forrige + runde, men er ikke formelt bekreftet som byggeretning. Brukeren + påpekte selv at dette ligger i grenselandet mot "individuelle + turneringer"-punktet rett over — notert eksplisitt som en mulig + forening av de to, ikke to separate systemer. Se FEATURE_BACKLOG.md + for full detalj. Ren notat-runde, ingen kode, ingen ADR. + +- **ADR-037 skrevet: grunnstruktur for individuelle/flerrunde-turneringer + (2026-07-26), samme dag som de to foregående notat-rundene.** Bruker ba + om å starte ADR-runden på strukturspørsmålet direkte (ikke bare + notere). Fire load-bærende beslutninger avklart eksplisitt + (AskUserQuestion), i rekkefølge: (1) ny, PARALLELL org-scopet + datamodell for individuelle turneringer — IKKE en utvidelse av + ADR-033s frittstående `round`-tabeller, bevisst for å unngå hybrid/ + betinget RLS (samme risikoklasse som den tidligere RLS-tomstreng- + bugen) og for å bevare ADR-033 Beslutning A urørt; (2) SAMME + `tournament`-tabell med en ny `format_type`-diskriminator + (`'team'`/`'individual'`), ikke en helt ny toppnivå-entitet — + gjenbruker synlighet/join-kode/status (ADR-018/020) uendret; (3) + flerrunde-støtte fra START via en ny `tournament_round`-tabell + (økt-lignende), sammenlagt resultat summert ved lesing på ekte + deltaker-id (samme prinsipp som dagens lag-leaderboard); (4) rå + brutto slagtall lagres OG et ferdig utregnet poengtall CACHES per + scoringsmetode — samme etablerte mønster som `match.status_text`/ + `points_side_a/b` — nye formater (Københavner m.fl.) blir dermed i + hovedsak én ny motorfunksjon + én ny CHECK-verdi, ikke en + skjemaendring. + **Viktig presisering som oppsto underveis, endrer forrige rundes + antakelse:** "flight" i en formell org-turnering er KUN en + tee-tid-gruppering — leaderboardet spenner alltid HELE feltet. + Dette er strukturelt ULIKT den ad hoc "flere flighter i en + frittstående runde"-ideen (der leaderboardet bevisst avgrenses til + det man selv satte opp) — de to holdes derfor bevisst ADSKILT, ikke + forent slik forrige runde antydet. Begge berørte FEATURE_BACKLOG.md- + seksjoner oppdatert med denne presiseringen. + **Fortsatt IKKE avgjort:** de fem konkrete formatenes egne poengregler + (Københavner m.fl., egne mindre design-runder oppå denne strukturen), + og Order of Merit (sesong-sammenlagt, bekreftet som naturlig SISTE + steg). **Ingen migrasjon eller kode skrevet** — ADR-037 er ren + struktur-beslutning; neste steg er et konkret migrasjonsutkast + (nye tabeller + `tournament.format_type`) lagt frem til gjennomgang + før noe kjøres. + +- **"Fiks alle kjente små bugs"-runde, BEGGE FIKSET OG SCRATCH-VERIFISERT + (2026-07-26), samme dag som ADR-037:** brukeren ba eksplisitt om å + rydde opp i alle dokumenterte, fortsatt åpne små bugs. Systematisk + gjennomgang av CLAUDE.md/FEATURE_BACKLOG.md (søk på "IKKE fikset"/ + "urelatert bug" m.fl.) fant at nesten alt tidligere flagget som + "ikke fikset" faktisk ble fikset i en SENERE runde lenger ned i loggen + (stale seksjonsoverskrifter, ikke reelt åpne bugs) — kun to genuint + fortsatt åpne: + 1. **Dashbord-hilsen brukte fullt navn** (nettopp etablert + navneformat-regel, se egen seksjon over) — `me.first_name ?? + me.display_name` i stedet for `me.display_name` alene. + `/auth/me` eksponerte allerede `first_name`, ingen backend-endring. + 2. **Stroke-modus-scoreinnsending på en bane UTEN registrerte hull + krasjet rått (500 IndexError)** i stedet for en ren + `VALIDATION_FAILED` — `scoring.py` sin `_compute_hole_results` + kalte `allocate_over_played_holes` med en TOM stroke-indeks-liste + når `hole`-tabellen var tom for banen (f.eks. en egendefinert bane + der noen glemte å legge til de 18 hullene). Fikset med en eksplisitt + `len(all_18_si) != 18`-sjekk RETT FØR beregningen — samme mønster + som den allerede kjente/fikset manglende-handicap-indeks-krasjen + fra blind draw-runden (2026-07-18). + **Scratch-verifisert grundig (5/5 sjekker)** for backend-fiksen + (isolert `teecup_app_scratch`-rolle + isolert scratch-MinIO + engangs + API-container, samme mønster som ellers i prosjektet): en bane UTEN + hull ga korrekt `400 VALIDATION_FAILED` ved scoreinnsending (ikke + 500), OG en egen regresjonssjekk bekreftet at en NORMAL bane (alle 18 + hull) fortsatt scorer helt uendret (begge sider, full + scorekort-henting via `GET .../scorecard`). `test_isolation.sql` + 12/12 uendret — ren Python-logikk-fiks, ingen migrasjon. Ekte + typesjekket produksjonsbuild av frontend kompilerte rent (dashbord- + fiksen). To stale FEATURE_BACKLOG.md-seksjonsoverskrifter rettet + samtidig ("Rapporterte UI-/UX-hull" og selve bug-notatet), siden de + fortsatt sa "IKKE fikset" lenge etter at innholdet faktisk var + fikset. + **Rullet ut live 2026-07-26**, bruker bekreftet eksplisitt: ingen + migrasjon, `docker compose up -d --build teecup_api teecup_frontend`, + begge containere boot-et rent (`Application startup complete`, Next.js + `Ready`), `/health`/`/dashboard` → 200, `teeoff.no` upåvirket. + Neste steg: 0. **Venter på brukerens bekreftelse for utrulling (2026-07-26):** varsler (in-app varslingssenter) + leaderboard-BACKENDEN for diff --git a/FEATURE_BACKLOG.md b/FEATURE_BACKLOG.md index 2b8b283..5d917db 100644 --- a/FEATURE_BACKLOG.md +++ b/FEATURE_BACKLOG.md @@ -574,6 +574,101 @@ handicap-/slagfordelingslogikken for disse fire formatene, se CLAUDE.md sin "Autoritative kilder"-seksjon. +### Utvidelse 2026-07-26: individuelle turneringer, flerrunde-turneringer, og Order of Merit + +Reist av brukeren som svar på et spørsmål om et individuelt +turnering-leaderboard — svaret avdekket at ønsket er STØRRE enn bare +Københavner som ett format blant flere: TeeCup skal etter hvert kunne +arrangere ekte INDIVIDUELLE turneringer (ikke bare lagturneringer), disse +skal kunne gå over FLERE RUNDER, og det skal være mulig å sette opp et +**Order of Merit** — sesong-sammenlagt poeng/rangering på tvers av flere +separate arrangementer (brukerens eget eksempel: "klubbdager" som gjentas +gjennom en hel sesong, med en løpende sammenlagt-tabell). **Ren notat- +runde, ingen kode skrevet, ingen ADR skrevet ennå** — brukeren ba +eksplisitt om at dette kun noteres nå. + +Tre distinkte, men beslektede strukturelle spørsmål — bevisst holdt fra +hverandre siden de har ulik arkitektonisk tyngde: + +1. **Individuelle turneringer (ingen lag i det hele tatt).** ADR-011 + låser v1 til NØYAKTIG to lag — en individuell turnering (et flatt felt + av spillere, som Københavner) bryter denne forutsetningen helt, ikke + bare "trenger flere enn to lag". Dette er allerede notert i ADR-011 + sitt eget "Merk (2026-07-19)"-avsnitt via Københavner-eksemplet, men + brukerens presisering nå gjør det tydelig at individuelle turneringer + er et EGET, generelt tilfelle — ikke bare én formatvariant blant + fem. Trenger en egen ADR som avklarer om dette blir en helt egen + turnering-TYPE (parallell til dagens to-lags-type, med sin egen + `match`/scoring-modell) eller en utvidelse av eksisterende modell. + +2. **Flerrunde-turneringer (samme arrangement, flere runder/dager, + sammenlagt resultat).** Dagens lagturneringer har ALLEREDE flere + `session`-er (f.eks. en Ryder Cup-helg med økter fredag/lørdag/søndag) + — men poengsummeringen er PER MATCH innad i hver økt, ikke en + sammenlagt SLAGSUM på tvers av runder slik en individuell + flerrunde-turnering (f.eks. en 3-dagers slagspillturnering) ville + trengt. **Reell strukturell kollisjon å avklare:** frittstående runder + (ADR-033 sin `round`/`round_participant`/`round_hole`, Beslutning A) + er BEVISST bygget helt UTENFOR organisasjon/RLS-systemet + (`plain_connection()`, eid av `user_id`, ingen `organization_id` i + det hele tatt) — mens turneringer er strengt org-scopet og RLS- + beskyttet. En org-arrangert flerrunde-individuell-turnering trenger + runder som lever INNENFOR en organisasjons/turnerings-kontekst — dette + er IKKE det samme systemet som de personlige rundene, selv om + datamodellen (hull-for-hull-registrering) sannsynligvis ligner mye. + Må avklares eksplisitt: gjenbruke `round`-tabellene (utvidet med en + valgfri turnering-/org-kobling), eller bygge en parallell, org-scopet + rundemodell? Dette er trolig den vanskeligste enkeltbeslutningen av de + tre. + +3. **Order of Merit (sesong-sammenlagt på tvers av FLERE separate + turneringer/arrangementer).** Krever et HELT NYTT overordnet konsept + som ikke finnes i skjemaet i dag — noe a la en "sesong" eller "serie" + som grupperer flere separate turnering-rader og akkumulerer poeng per + spiller på tvers av dem, med sin egen løpende sammenlagt-rangering. + Forutsetter sannsynligvis at (1) og (2) over er løst først (det er + individuelle arrangementer som skal telle inn i et Order of Merit, + ikke lag-baserte Ryder Cup-turneringer) — naturlig SISTE steg av de + tre, ikke noe som kan designes isolert. + +**Ingenting av dette er designet eller bygget** — kun fanget presist her +slik at retningen er dokumentert før noe glemmes. Se også ADR-011 sitt +eget notat (samme sak, kortere) og "Åpne spørsmål"-listen i +ARCHITECTURE_DECISIONS.md. + +### Oppdatering 2026-07-26, samme dag: grunnstruktur for (1) og (2) AVKLART — se ADR-037 + +Brukeren ba om å starte ADR-runden på strukturspørsmålet direkte. Fire +load-bærende beslutninger avklart eksplisitt (AskUserQuestion), full +begrunnelse i **ADR-037** (ARCHITECTURE_DECISIONS.md): +- **Ny, parallell org-scopet datamodell** — IKKE en utvidelse av + ADR-033s `round`-tabeller (unngår hybrid/betinget RLS, bevarer et + tidligere bevisst valg). +- **Samme `tournament`-tabell**, ny `format_type`-diskriminator + (`'team'`/`'individual'`) — gjenbruker synlighet/join-kode/status + helt uendret. +- **Flerrunde fra start**: ny `tournament_round`-tabell (økt-lignende), + sammenlagt resultat summert ved lesing på ekte deltaker-id (samme + prinsipp som det eksisterende lag-leaderboardet). +- **Rå slag lagres OG poeng caches per format** — samme mønster som + `match.points_side_a/b` i dag. Nye formater (Københavner m.fl.) blir + dermed i hovedsak: én ny motorfunksjon i `handicap_engine.py` + én ny + CHECK-verdi, ikke en skjemaendring. + +**Viktig presisering som oppsto underveis:** "flight" i en formell +org-turnering er KUN en tee-tid-gruppering, IKKE en leaderboard-grense +(leaderboardet spenner alltid hele feltet) — ULIKT den ad hoc +"flere flighter i en frittstående runde"-ideen (der leaderboardet +bevisst er avgrenset til det man selv satte opp). De to holdes bevisst +ADSKILT nå, ikke forent slik forrige runde antydet — se egen seksjon +lenger ned i denne filen ("Frittstående runder: flere flighter..."). + +**Fortsatt IKKE avgjort:** de fem konkrete formatenes egne poengregler +(punkt utenfor denne strukturrunden), og (3) Order of Merit — bekreftet +som naturlig SISTE steg, ikke designet. **Ingen migrasjon skrevet** — +ADR-037 er ren struktur-beslutning, neste steg er et konkret +migrasjonsutkast til gjennomgang. + --- ## Varsler: push til telefon + in-app varslingssenter — 📋 DESIGNET 2026-07-25, IKKE bygget @@ -1355,16 +1450,23 @@ en reell skjemamigrasjon (`014_tee_gender_to_rating.sql`). IDENTISK bruttoscore (5-5) på begge sider ga et ikke-delt resultat ("a" vant, ikke "halved") — direkte bevis på at handicap nå faktisk brukes. Ingen migrasjon (ren Python-logikk-fiks). - **Ekte, urelatert bug funnet UNDER samme scratch-test, IKKE fikset - ennå:** et stroke-modus scoreinnsending på en bane UTEN registrerte - hull (`hole`-tabellen tom) krasjer med en rå 500 + **✅ FIKSET 2026-07-26** (i en egen "fiks alle kjente små bugs"-runde, + lenge etter oppdagelsen over): et stroke-modus scoreinnsending på en + bane UTEN registrerte hull (`hole`-tabellen tom) krasjet med en rå 500 (`IndexError: list index out of range` i `handicap_engine.py` sin `allocate_over_played_holes`, kalt fra `scoring.py` sin `_compute_hole_results`) i stedet for en tydelig `VALIDATION_FAILED`. - Samme klasse feil som den allerede kjente/fikset - manglende-handicap-indeks-krasjen fra blind draw-runden (2026-07-18) — - bør fikses likt (eksplisitt sjekk FØR beregning, ikke en try/except - rundt symptomet). Ikke fikset i denne runden, kun oppdaget og notert. + Fikset akkurat som foreslått da bugen ble oppdaget: eksplisitt sjekk + (`len(all_18_si) != 18`) rett før `allocate_over_played_holes` kalles, + ikke en try/except rundt symptomet — samme mønster som den allerede + kjente/fikset manglende-handicap-indeks-krasjen fra blind draw-runden + (2026-07-18). Scratch-verifisert presist (5/5 sjekker): en bane UTEN + hull gir nå ren `400 VALIDATION_FAILED` ved hull-scoreinnsending i + stedet for 500, OG en regresjonssjekk bekreftet at en NORMAL bane + (med alle 18 hull) fortsatt scorer helt uendret (begge sider, full + scorekort-henting). `test_isolation.sql` 12/12 uendret (ren + Python-logikk-fiks, ingen migrasjon). Rullet ut sammen med + dashbord-hilsen-fiksen under, se CLAUDE.md-status. **Designspørsmålene for punkt 2 er avklart** (bruker valgte det anbefalte alternativet på begge, se ADR-029 Beslutning B/C): helautomatisk tee-valg @@ -1381,7 +1483,7 @@ sammenslåingen. --- -## Rapporterte UI-/UX-hull (2026-07-19) — notert, IKKE fikset ennå +## Rapporterte UI-/UX-hull (2026-07-19) — ✅ ALLE FIRE FIKSET (siste 2026-07-21) Fire punkter rapportert av brukeren fra faktisk bruk av `teecup.teeoff.no`. Root cause funnet ved kodegjennomgang for de tre første (ikke bare gjettet); @@ -2084,7 +2186,7 @@ første er tryggere (ingen delvis fullført tilstand ved feil midtveis). --- -## Frittstående runder: flere flighter i én "vanlig" runde — 📋 NOTERT 2026-07-25, IKKE bygget +## Frittstående runder: flere flighter i én "vanlig" runde — 📋 NOTERT OG PRESISERT 2026-07-25/26, IKKE besluttet eller bygget Reist av brukeren samme runde: eksempel gitt — "jeg går i den første flighten sammen med tre andre, mens fire venner går i flighten bak." @@ -2115,6 +2217,53 @@ spørsmål. Henger dessuten sammen med det pågående ADR-036-arbeidet ADR-036 er bygget, siden "hvem er i min flight" og "hvem er min venn/ medspiller" er beslektede, men ikke identiske spørsmål. +### Presisert 2026-07-26 (fortsatt IKKE besluttet, kun tydeligere) + +Brukeren presiserte tre konkrete ting ved oppfølging: + +1. **Oppsettflyten er ÉN handling utført av ÉN person.** "Når jeg setter + opp en vanlig runde kan jeg sette opp for bare meg, for inntil 3 andre + i samme flight, eller for flere flighter." Det er brukeren selv som + tar ansvar for å sette opp ALLE flightene (også vennenes, i eksemplet) + — ikke at hver flight settes opp uavhengig av sine egne deltakere. + Speiler dagens gjest-mønster (eieren legger til gjester), bare + utvidet til å dekke flere adskilte flight-grupper i samme handling. +2. **Fremtidig, ikke motstridende:** "det er selvfølgelig de i flightene + bak som må føre sin egen score" — bekrefter at ansvaret for å SETTE + OPP og ansvaret for å REGISTRERE SCORE er to forskjellige ting, og at + det andre (scoreregistrering per flight) forventes løst av ekte + medspillere med konto (ADR-036 fase 3), ikke av oppsetteren manuelt. +3. **Leaderboard-omfanget er PRESIST det som ble satt opp SAMMEN, ikke + "alle som spilte samme bane samme dag":** brukerens eget eksempel — + setter Alice opp en flight med fire, og vennene deres setter opp en + HELT ANNEN flight med fire (uavhengig av Alices oppsett), skal disse + to leaderboardene IKKE slås sammen. Kun flightene som ble satt opp + SAMMEN i én handling deler leaderboard. + +**Konsekvens for de to retningene:** punkt 3 er et sterkt signal FOR +retning 1 (løs gruppering av separate `round`-rader, bundet sammen av et +tynt "satt opp sammen"-konsept som samtidig er leaderboardets naturlige +omfang) — retning 2 (én `round` som beholder) ville krevd en ekstra +mekanisme for AKKURAT denne avgrensningen uansett, siden "alle som +spilte samme bane samme dag" aldri var riktig omfang i utgangspunktet. +**Fortsatt IKKE en endelig beslutning** — brukeren utforsket/presiserte +forståelse, bekreftet ikke eksplisitt en byggeretning. + +**Brukerens egen observasjon, viktig å fange presist:** "jeg ser veldig +godt at jeg opererer i grenseland mellom frittstående runde og +turneringer her." Dette pekte først mot en mulig forening med +"Individuelle turneringer, flerrunde-turneringer og Order of Merit" — +**men presisert og IKKE lenger antatt samme konsept, se ADR-037 +(2026-07-26):** i en formell org-turnering er "flight" KUN en +tee-tid-gruppering, og leaderboardet spenner alltid HELE feltet +uavhengig av hvem som spilte sammen. Her, i den ad hoc frittstående +runden, skal leaderboardet AVGRENSES til nøyaktig det som ble satt opp +sammen. De to "flight"-begrepene ligner i UI (flere grupper spiller +samme dag), men er strukturelt ulike leaderboard-omfang — holdes derfor +bevisst ADSKILT (to separate, mindre systemer), ikke forent til ett. +Denne seksjonens egen retning (løs gruppering av separate `round`-rader, +se over) står fortsatt som anbefaling, uendret av presiseringen. + --- ## Frittstående rundeføring + detaljert statistikk (uten turnering/organisasjon) — ✅ BACKEND + FRONTEND LIVE, løpende oppfølging t.o.m. 2026-07-25 (se ADR-033 for full detalj per dag) diff --git a/app/routers/scoring.py b/app/routers/scoring.py index b31a6e4..9d1ae66 100644 --- a/app/routers/scoring.py +++ b/app/routers/scoring.py @@ -131,6 +131,15 @@ async def _compute_hole_results(conn, match_id: str, match) -> list[HoleResult]: match["course_id"], ) all_18_si = [r["stroke_index"] for r in stroke_index_rows] + # Uten alle 18 hull (f.eks. en egendefinert bane der hull aldri ble lagt + # til) kan slagfordelingen (allocate_over_played_holes) ikke gjøres -- + # avvis tydelig FØR beregning i stedet for å krasje rått på tom liste. + if len(all_18_si) != 18: + raise app_error( + 400, + "VALIDATION_FAILED", + "Banen mangler registrerte hull -- kan ikke beregne slagfordeling for stroke-modus.", + ) strokes_per_hole: dict[str, dict[int, int]] = { unit: dict(zip(played, allocate_over_played_holes(total, all_18_si, played))) diff --git a/frontend/components/dashboard.tsx b/frontend/components/dashboard.tsx index 6a235ed..9a1da42 100644 --- a/frontend/components/dashboard.tsx +++ b/frontend/components/dashboard.tsx @@ -68,6 +68,7 @@ type Me = { id: string email: string display_name: string + first_name: string | null preferred_locale: string profile_complete: boolean handicap_index: number | null @@ -374,7 +375,7 @@ export function Dashboard() {

- God dag, {me.display_name} + God dag, {me.first_name ?? me.display_name}

Klar for en ny runde? Her er golfen din på ett sted.