**Schwächere Rolle sperrte stärkere Rolle aus (Mehrfachrollen)**: Wer neben einer Vereinsmanager-Rolle zusätzlich Admin, SBK, RSK oder Ansetzer war, wurde an mehreren Stellen von der eigenen schwächeren Rolle blockiert, weil die Berechtigungsprüfungen als `if/elsif`-Ketten formuliert waren und nach dem ersten passenden Zweig abbrachen. Aufgefallen war das beim Direkt-Transfer: Eine SBK, die zugleich Vereinsmanagerin eines anderen Vereins ist, bekam schon bei der Spielersuche „Nicht berechtigt für diesen Verein", obwohl der Direkt-Transfer selbst (`direct_assign`) den Wechsel erlaubt hätte. Alle Rollen werden jetzt additiv ausgewertet: Transfer- und Korrekturanträge (Suche, Anlage, Listen), Vereins- und Spielbetriebslisten, Lizenz-Ligenliste, Team- und Lizenz-Aktionen, Spielberichts-/Aufstellungsrechte sowie das Schiedsrichter-Scoping. Vereine, die nur über die VM-Rolle erreichbar sind, erscheinen in der Vereinsauswahl jetzt unter „Eigene Vereine". Betroffen war auch die Benutzerliste sowie das Speichern von Spielbericht-Feldern, wo eine SBK-Rolle aus einem anderen Verband den Zugriff über eine passende VM-/TM-Rolle verdeckte.
**Aussagekräftigere 404-Meldung bei `game_operations/:id/...`**: Wurde einem Spielbetriebs-Endpunkt (z. B. `game_operations/:id/clubs`) versehentlich eine Liga-ID statt einer GameOperation-ID übergeben, lautete die Antwort nur „Spielbetrieb nicht gefunden" – irreführend, weil unter der ID sehr wohl eine Liga existiert. Die Meldung nennt jetzt die geprüfte ID und weist darauf hin, dass eine GameOperation-ID (klein) erwartet wird und Liga-Endpunkte unter `/leagues/:id` liegen. Zusätzlich wurden tote RESTful-Routen von `resources :game_operations` (show/create/update/destroy/new/edit ohne Controller-Action) entfernt (API #193).
**Gastteam-Name auf der Team-Seite wieder sichtbar**: In den Listen „Kommende Spiele" und „Letzte Spiele" der öffentlichen Team-Seite war der Name der Gastmannschaft praktisch unsichtbar, weil er in der schmalen Logo-Spalte statt in der dafür vorgesehenen breiten Spalte lag. Der Name steht jetzt in seiner eigenen Spalte (Idee und Erstumsetzung: @mafo5, Frontend #127).
**Login-Sperre blockierte Teammanager mit Schiedsrichter-Rolle**: Ein/e Nutzer:in mit Teammanager-Rolle, aber (noch) ohne Team in der aktuellen Saison wurde beim Login mit „Keine Teams in der aktuellen Saison." abgewiesen, selbst wenn zusätzlich eine RSK-, Ansetzer- oder Schiedsrichter-Rolle vorlag. Die TM-Login-Sperre greift jetzt nur noch, wenn wirklich keine andere nutzbare Rolle vorhanden ist; RSK, Ansetzer und die Schiedsrichter-Selfservice-Rolle bleiben ausgenommen.
**Strafen-Katalog auf aktuellen Regelstand gebracht**: Das „Strafe"-Dropdown im Spielbericht (`Setting#penalties`, ohne Admin-Oberfläche) enthielt noch veraltete Einträge. Eine einmalige, idempotente Rake-Task (`penalties:normalize_catalog`, Dry-Run als Standard) blendet „5 Minuten" sowie „Spielstrafe 1/2/3" aus (als `disabled`, nicht gelöscht, damit historische Ereignis-Referenzen erhalten bleiben) und benennt „Spielstrafe"/„Spieldauerstrafe (tech.)" in „Matchstrafe"/„Matchstrafe (technisch)" um. Aktiver Katalog danach: 2 Minuten, 2+2 Minuten, 10 Minuten, Matchstrafe, Matchstrafe (technisch). Adressiert über das stabile `mapping`, damit prod- und staging-unabhängig.
**Benachrichtigung bei Spieländerungen nach veröffentlichter Ansetzung**: Ändert sich bei einem Spiel mit bereits veröffentlichter (persönlicher) Schiedsrichter-Ansetzung der Anpfiff, das Spieltagsdatum, die Halle oder der Absage-Status, erhalten die angesetzten Schiedsrichter, der Schiedsrichtercoach sowie der Ausrichter jetzt automatisch eine Änderungs-Benachrichtigung. Bisher blieb eine solche Änderung unkommuniziert, obwohl die Beteiligten bei der Veröffentlichung bereits informiert worden waren. Die Live-Erfassung (Spielbericht) und andere Feldänderungen lösen bewusst keine Mail aus (nur echte Änderungen an Anpfiff, Absage, Datum oder Halle).
**Anpfiff in den Ansetzungs-Mails**: Die Ansetzungs- und Änderungs-Benachrichtigungen an Schiedsrichter, Coach und Ausrichter enthielten bisher nur das Datum, nicht die Startzeit des Spiels. Der Anpfiff wird jetzt zusätzlich ausgewiesen.
Neu
**E-Mail-Adresse in der Schiedsrichter-Liste** (`admin/referees`): Die Übersicht liefert pro Schiedsrichter jetzt zusätzlich die hinterlegte `email`, bisher war sie nur in der Detailansicht (`admin/referees/:id`) enthalten. Damit kann das Frontend die Liste inklusive Kontaktadressen als CSV exportieren. Sichtbar für Admin, RSK und Ansetzer – also genau die Rollen, die die Schiedsrichterverwaltung ohnehin öffnen; ein reiner Vereinsmanager erhält die Liste weiterhin ohne Adressen.
**Schiri „Meine Spieltage": Verlinkung zur Spielseite vorbereitet**: Der Endpoint `referee/game_days` liefert pro Spieltag jetzt zusätzlich `league_id` und `game_operation_slug`, damit das Frontend aus „Meine Spieltage" direkt zur (öffentlichen) Spielseite verlinken kann (`/:association/:leagueId/spiel/:matchId`) – bisher lag nur der Liganame als Text vor (API #189).
**Neuer Endpoint: alle Spiele eines Teams wettbewerbsübergreifend** (`GET /api/v2/teams/:id/matches`): Liefert alle Partien eines Teams über sämtliche Wettbewerbe seiner Saison (Haupt-/Aufstiegsrunde, Playoffs, Pokal …) als strukturierte JSON-Liste – gespielte und kommende Spiele, jeweils mit Datum/Anpfiff, Heim-/Gastteam samt IDs, Ergebnis, Status (`scheduled`/`running`/`finished`/`cancelled`), Halle und Wettbewerbszuordnung (`league_id`/`league_name`). Ergänzt den bisherigen iCal-Export (`teams/:id`), aus dem sich Ergebnisse, Status und Wettbewerb nicht sauber strukturiert auslesen ließen. Öffentlich (X-Api-Key) (API #194).
**Team-IDs im Ligaspielplan (`schedule.json`)**: Die Antwort von `leagues/:id/schedule.json` (und der Spieltags-Varianten) enthält pro Partie jetzt zusätzlich `home_team_id`/`guest_team_id` sowie `home_team_club_id`/`guest_team_club_id`. Bisher gab es nur die Teamnamen, was API-Nutzer:innen zu fragilen Zuordnungen über den Namen (bzw. zum Parsen der Team-ID aus dem Logo-Dateinamen) zwang. Die Ergänzung ist rein additiv und abwärtskompatibel (API #195).
**Team-Seite: Spiele direkt anklickbar**: In den Listen „Kommende Spiele" und „Letzte Spiele" der öffentlichen Team-Seite führt jetzt jede Zeile per Klick direkt zur jeweiligen Spielseite. Der `teams#stats`-Endpoint liefert dafür pro Spiel zusätzlich die `league_id`, damit das Frontend auch bei Teams mit Spielen in mehreren Ligen (z. B. Vor-/Rückrunde, Playoffs) den korrekten Liga-Link bilden kann (Idee und Erstumsetzung: @mafo5, Frontend #127).
**Schiri-Feedback: Kommentar-Feed und Themen-Tags**: Die Freitextkommentare aus dem Vereins-Feedback (Linie, Kommunikation, allgemein) sind jetzt übergreifend auswertbar statt nur einzeln am Schiri-Profil. Ein neuer Feed (`admin/feedback_comments`) listet alle kommentierten Rückmeldungen, filterbar nach Schiri, Top-Gruppe (Schiri-Tag), Liga, Saison, Zeitraum, Notenschwelle und Thema; ausgeblendete Rückmeldungen bleiben außen vor. Über eine admin-gepflegte Themen-Taxonomie (`admin/feedback_themes`, z. B. Positionierung, Regelauslegung, Auftreten) taggen RSK und Ansetzer die Kommentare händisch. Dadurch werden Themen zählbar: eine Auswertung (`admin/feedback_comments/stats`) liefert Häufigkeiten je Thema (gesamt, für die Top-Gruppe und per Schiri-Filter) als Ranking sowie eine Monats-Zeitreihe, um die Wirkung des Coachings zu beurteilen. Sichtbarkeit wie bisher: Admin, FD-RSK und FD-Ansetzer (global) (API #182).
**Schiri-Feedback-Auswertung über alle Schiedsrichter**: Neuer Auswertungs-Endpoint (`admin/referee_feedback_analytics`), der das Vereins-Feedback nicht mehr nur einzeln am Schiri-Profil, sondern übergreifend aggregiert: mit Gesamt- und Gruppen-Mittelwert für Spielleitung und Kommunikation, einer Schiri-Tabelle (Anzahl, Ø-Werte, Trend zur Vorperiode), Notenband-Verteilung und Monats-Zeitreihe. Filterbar nach Saison, Liga, Zeitraum und einer per Schiri-Tag definierten Top-Gruppe; zusätzlich nach Ausgang des bewertenden Teams (gewonnen/verloren), um den Bewerter-Bias sichtbar zu machen. Schiris unterhalb einer Mindest-Fallzahl werden als „nicht rankbar" markiert, die Anzahl steht immer dabei. In die Kennzahlen fließen nur sichtbare (nicht moderierte) Rückmeldungen. Die Kennzahlen sind zusätzlich als CSV/Excel exportierbar. Sichtbarkeit wie bisher: Admin, FD-RSK und FD-Ansetzer (global) (API #181).
Verbessert
**Ansetzungs-Verlauf zeigt die tatsächlich eingesetzten Schiedsrichter**: Die Liste der Ansetzungen (`admin/referee_assignments`) liefert pro Ansetzung jetzt zusätzlich das Feld `officials` mit den laut Spielbericht wirklich eingesetzten Schiedsrichtern (positionstreu je Slot, aufgelöst über `officiating_referee_ids` mit Rückfall auf den Klartext-Namen aus dem Bericht). Im Rückblick auf vergangene Spiele stand bisher nur die ursprüngliche Ansetzung, die von der tatsächlichen Besetzung abweichen kann. Die Auflösung erfolgt gebündelt für die ganze Liste, damit keine Abfrage pro Spiel entsteht. Rein additiv; die Einzel-Antworten beim Anlegen, Ändern, Benachrichtigen und Veröffentlichen liefern eine leere Liste.
**Staging spiegelt Prod jetzt 1:1 (echte Logins)**: Der DB-Refresh von `saisonmanager.dev` anonymisiert die Prod-Daten nicht mehr. Damit können sich Produktiv-Nutzer:innen auf der Staging-Umgebung mit ihrem echten Benutzerkonto und Passwort anmelden (weiterhin vorgelagert durch Basic Auth geschützt; ausgehende Mails fängt Mailpit ab). Der bisherige, ausschließlich für Staging bestimmte Anonymisierungs-Task `staging:anonymize` entfällt und wird durch zwei neue Staging-Tasks ersetzt (beide mit Host-Schutzprüfung, laufen nur gegen die Staging-DB): `staging:prune_limited_users` entfernt nach dem Klon alle Benutzerkonten, die ausschließlich Vereinsmanager, Teammanager oder Schiedsrichter sind (Datenminimierung – nur administrative Konten bleiben als echte Logins). `staging:seed_demo_users` legt anschließend die kuratierten Demo-Konten (ein Login je Rolle, bekanntes Passwort) an, damit sich auch die entfernten Rollen weiterhin testen lassen.
**Eigenes Passwort für einzelne Staging-Demo-Konten**: `staging:seed_demo_users` setzte bisher für alle `demo_*`-Konten dasselbe Passwort. Eine Definition in `db/staging_demo_users.json` kann jetzt `password_env` angeben; der Wert wird dann aus der genannten Umgebungsvariablen gelesen (Rückfall auf das gemeinsame Passwort samt Warnung, wenn sie fehlt). Genutzt für `demo_admin` (`STAGING_ADMIN_PASSWORD`), damit das Konto mit Vollzugriff nicht dasselbe Passwort wie die Rollen-Testkonten trägt. Die Passwörter selbst stehen nicht im Repository, sondern in der gitignorierten `.env` des Docker-Setups, die `staging-db-refresh.sh` durchreicht. Die Abschluss-Ausgabe listet die gesetzten Passwörter jetzt gruppiert nach Konten.
**Benutzernamen kleinschreibungsneutral (Login mit Groß- und Kleinschreibung)**: Der Login akzeptiert den Benutzernamen jetzt unabhängig von der Groß-/Kleinschreibung (Vergleich gegen `LOWER(user_name)`), sodass auch Bestandskonten mit Großbuchstaben im Namen weiterhin per Benutzername anmeldbar sind. Der Benutzername wird nicht mehr zwangsweise klein geschrieben, sondern nur um Rand-Leerzeichen bereinigt; erlaubt sind Buchstaben (groß und klein), Ziffern, Punkt, Bindestrich und Unterstrich (Empfehlung: `vorname.nachname`). Umlaute (ä, ö, ü) und ß werden beim Anlegen oder Ändern mit einer klaren Fehlermeldung abgelehnt. Doppelte Benutzernamen werden kleinschreibungsneutral erkannt, sodass sich „Max" und „max" nicht gleichzeitig anlegen lassen. Der Passwort-Reset findet Konten ebenfalls kleinschreibungsneutral. Bestandskonten bleiben unangetastet, die Format-Prüfung greift nur beim Setzen oder Ändern des Namens.