ADR-0022: Ein Beobachter spielt nicht¶
- Status: accepted
- Datum: 2026-09-16
- Betrifft: Datenmodell, Authentifizierung, Oberfläche, Schnittstellen
- Bezug: schreibt
0008-wer-fuer-andere-handeln-darffort und ersetzt es nicht — das Admin-Flag bleibt eine Stufe, keine Rolle. Berührt0015-zaehlwerk-traegt-ergebnisse-einbeiGET /api/players. Issue #271.
Kontext¶
ADR-0008 hat players.is_admin eingeführt, und die Oberfläche unter /admin
existiert: Ergebnisse entfernen, Spieler entfernen, Kiosk-Freigaben
zurücknehmen. Im Büro hat das Flag niemand. Als am 2026-09-16 ein
Test-Match aus dem Zählwerk wieder heraus musste, lief das deshalb als
SQL-Transaktion von Hand (stuttgart-things#2997) — genau der Weg über psql,
den ADR-0008 abschaffen wollte.
Das Flag einem der Spieler zu geben liegt nahe und passt nicht. Wer ein
gewertetes Ergebnis entfernt, soll nicht zugleich eigene Ergebnisse in
derselben Tabelle haben; die Frage „hat da jemand sein eigenes Match
korrigiert" soll sich gar nicht stellen. Gewünscht ist ein eigenes Konto,
das verwaltet und nie spielt — Name timoboll.
Ein solches Konto ist heute ein gewöhnlicher Spieler. Es stünde mit TTR 1000 in
der Rangliste, wäre als Gegner wählbar, im Turnier aufstellbar und läge über
GET /api/players in der Operator-Auswahl des Zählwerks.
Entscheidung¶
Ein zweites Flag am Spieler, players.is_observer: wer es trägt, spielt
nicht. Es sagt nichts über Rechte.
Was ein Beobachter nicht ist¶
- Nicht in der Rangliste und nicht in den Statistiken.
- Nicht wählbar als Spieler: nicht als Gegner beim Eintragen, nicht im Kiosk als einer der beiden Spieler, nicht im Turnier.
- Nicht in
GET /api/players. Das Zählwerk bleibt, wie es ist. - Abgelehnt als Spieler in jedem schreibenden Weg — Spieler-Pfad, Kiosk,
Turnier,
POST /api/results. Die Oberfläche blendet ihn aus, die Prüfung sitzt trotzdem beim Schreiben, weil eine ausgeblendete Option keine Regel ist.
Was ein Beobachter bleibt¶
- Er meldet sich an wie jeder andere: Name zuerst, PIN (ADR-0007, ADR-0018).
Die Anmelde-Auswahl zeigt ihn deshalb weiter. „Alle Spieler" und „alle, die
spielen" sind ab hier zwei verschiedene Listen, und jede Stelle, die heute
Players().Listliest, muss sagen, welche sie meint. - Er darf Operator am Kiosk sein. Jemand, der zusieht und zählt, ist genau das, was ADR-0014 vom Operator verlangt.
- Er kann Admin sein.
timobollträgt beide Flags. Die beiden sind unabhängig: ein Beobachter ohne Admin-Rechte ist ein gültiger Fall, etwa ein Konto für jemanden, der nur zusieht.
Wer es setzen darf, und wann¶
- Ein Admin, unter
/admin, auch für das eigene Konto. Jede Änderung schreibt eine Logzeile mit beidenplayer_id, wie jede Admin-Handlung. - Nur für einen Spieler ohne ein einziges Match — gleich welcher Status — und ohne Turnierteilnahme. Eine Historie macht aus einem Spieler keinen Beobachter; sie verschwände sonst aus Rangliste und TTR-Verlauf anderer, die gegen ihn gespielt haben. Für diesen Fall ist Zusammenführen gedacht (ADR-0017, noch Entwurf), nicht dieses Flag.
- Zurücknehmen geht immer. Der Spieler taucht mit seinem unveränderten TTR wieder auf; da er nie gespielt hat, ist das 1000.
Der erste Beobachter¶
Kein neuer Bootstrap. Die Reihenfolge für timoboll:
- Das Konto tritt über die normale Oberfläche bei und setzt dabei seine eigene PIN.
SP_BOOTSTRAP_ADMIN=timoboll, Neustart — das Admin-Flag kommt wie in ADR-0008 vorgesehen.timobollsetzt unter/adminsich selbst als Beobachter.
Zwischen 1 und 3 steht das Konto kurz als Spieler ohne Matches am Ende der Rangliste. Wird es in diesen Minuten als Gegner gewählt, verweigert Schritt 3 das Flag, und das ist sichtbar statt still.
Warum nicht anders¶
Warum kein Wert in einer Rollen-Spalte. ADR-0008 sagt „ein Flag, keine
Rollen" und meint Stufen von Rechten. is_observer ist keine Stufe — es regelt
Teilnahme, nicht Befugnis, und steht quer zu is_admin. Eine Spalte
role in ('player', 'admin', 'observer') würde zwei unabhängige Fragen in eine
zwingen und timoboll genau den Fall verbieten, für den es gebraucht wird.
Warum der Beobachter nicht aus SP_BOOTSTRAP_ADMIN folgt. Wer das Büro
verwaltet, darf trotzdem mitspielen wollen. Beide Flags an eine Variable zu
koppeln, spart eine Zeile Oberfläche und nimmt diese Freiheit.
Warum keine eigene Variable SP_BOOTSTRAP_OBSERVER. Sie schlösse die
Lücke zwischen Beitritt und Flag, verlangte aber eine Konfiguration mehr an
vier Stellen (kcl, terraform, compose, argocd-Katalog) für einen Vorgang, der
einmal passiert. Die Lücke ist Minuten lang und fällt auf, wenn sie trifft.
Warum nicht beim Beitritt ankreuzbar. Dann setzt es sich jeder, der nicht in der Rangliste stehen will, und die Frage, wer mitspielt, beantwortet nicht mehr das Büro.
Warum kein Konto für das Zählwerk. Naheliegend, sobald es Konten gibt, die
nicht spielen: das Zählwerk als Beobachter, und reported_by wäre das
Zählwerk. ADR-0015 hat das bewusst anders entschieden, und daran ändert dieses
Flag nichts:
- Eine Maschine hat keine Identität. Das Zählwerk weist sich über sein
Token aus; die Herkunft steht in
entered_via = 'scoreboard'. Beides zusammen sagt schon, dass eine Maschine eingetragen hat. Ein Spieler-Konto sagte es ein drittes Mal, ohne etwas Neues zu wissen. reported_bybenennt den Menschen, der zugesehen hat. Stünde dort das Zählwerk, wäre die Frage „wer hat an diesem Abend gezählt" nicht mehr beantwortbar, und die Bestätigung hinge nicht mehr an einer Person.- Ein Konto braucht eine PIN, und eine PIN, die in einer Maschine steht, ist ein zweites Token mit schwächeren Eigenschaften.
Wenn es je mehrere Zählwerke gibt und eine Zeile sagen soll, welches eingetragen hat, ist die Antwort ein Token pro Gerät — wie die Kiosk-Freigaben aus #77 —, kein Spieler.
Konsequenzen¶
- Migration, additiv:
is_observer boolean not null default false(Invariante 8). Keine bestehende Zeile ändert sich. - Das Repository bekommt zwei Lesarten — alle Spieler und die, die spielen.
Jede heutige Stelle von
Players().ListundPlayers().Recordswird einer davon zugeordnet; Anmeldung und/adminlesen alle, alles andere die spielenden. - Die Regel „kein Beobachter als Spieler" sitzt beim Schreiben, in den
Repositories und nicht in den Handlern:
Matches().Createfragt im selben Insert,Tournaments().CreateundReplacevor dem ersten Schreiben. So schreiben Kiosk, Turnier, Spieler-Pfad und/apisie nicht viermal, und ein Flag, das einen Moment vorher gesetzt wurde, rutscht nicht zwischen Prüfung und Schreiben durch. SP_BOOTSTRAP_ADMINfehlt im argocd-Katalog-Chart (apps/schmetterpause/install), steht aber in kcl, terraform und compose. Ohne ihn lässt sich Schritt 2 auf homerun2-test1 nicht ausführen. Das ist eine eigene Lücke, die hier nur auffällt.
Was ausdrücklich wartet¶
- Ein Beobachter als Operator am Zählwerk.
GET /api/playersliefert keine Beobachter, also bietet das Zählwerktimobollnicht als Operator an. Das zu ändern heißt, dem Vertrag ein Feld zu geben, das das Zählwerk auswerten muss — eine Änderung in zwei Repositories, die erst lohnt, wenn jemand am Zählwerk als Beobachter zählen will. - Mehrere Zählwerke unterscheiden. Siehe oben; Token pro Gerät, wenn es nötig wird.