diff --git a/.planning/quick/260910-das-mandantentrennung-etappe-2-bereich-user-/260910-das-PLAN.md b/.planning/quick/260910-das-mandantentrennung-etappe-2-bereich-user-/260910-das-PLAN.md
index 1da0b93..c879e16 100644
--- a/.planning/quick/260910-das-mandantentrennung-etappe-2-bereich-user-/260910-das-PLAN.md
+++ b/.planning/quick/260910-das-mandantentrennung-etappe-2-bereich-user-/260910-das-PLAN.md
@@ -35,7 +35,7 @@ must_haves:
- "Die uebergreifende Sicht des Plattform-Administrators bleibt erhalten, ohne ungebunden zu sein: sie laeuft als Schleife ueber alle Mandanten mit je EINEM gebundenen Lesezugriff im Rumpf — dieselbe Form, die die Standardgruppen-Reparatur beim Start bereits benutzt. Dass der Schleifentreiber selbst ungebunden lesen darf, haengt daran, dass die Mandantentabelle keinen Zeilenschutz traegt, und das ist gemessen."
- "Eine bereits bestehende, heute wirksame Luecke ist geschlossen: der Riegel gegen das Loeschen des eigenen Kontos vergleicht gegen ein Feld, das der Sitzungsnachweis gar nicht traegt, und greift deshalb nie. Ein Administrator kann sich heute selbst loeschen und einen Mandanten ohne Verwaltung zuruecklassen."
- "Die Testlage dieses Bereichs ist repariert und die Form der Luecke ist benannt: zwei vorhandene Testdateien haben KEINE Attrappe fuer das Bindungshilfsmittel und waeren nach der Umstellung aus dem falschen Grund rot geworden, die Steuerungsschicht hat gar keine Testdatei. Nach diesem Durchlauf existiert je Datei ein Zwei-Klienten-Nachweis, dessen Rotwerden durch probeweisen Rueckbau belegt ist."
- - "Klassifikationsdokument und `rls-access-inventory.spec.ts` zeigen fuer alle Paare dieses Bereichs denselben, maschinell gemessenen Stand — einschliesslich des NEUEN Paares, das die Schleife ueber alle Mandanten in `user.service.ts` erzeugt. Uebersichtszeile, Summenzeile, Klassen-Verteilungstabelle samt ihrer Summe und der Abschnitt zur Hintergrunddienst-Falle sind von Hand nachgezogen; genau diese vier Stellen wurden im `dkv`-Durchlauf uebersehen."
+ - "Klassifikationsdokument und `rls-access-inventory.spec.ts` zeigen fuer alle Paare dieses Bereichs denselben, maschinell gemessenen Stand — einschliesslich des NEUEN Paares, das die Schleife ueber alle Mandanten in `user.service.ts` erzeugt. Uebersichtszeile, Summenzeile, Klassen-Verteilungstabelle samt ihrer Summe und der Abschnitt zur Hintergrunddienst-Falle sind von Hand nachgezogen — und jede der vier haengt an einer eigenen maschinellen Pruefung, die die Zahlen aus Quelltext beziehungsweise Bestandsaufnahme neu ableitet, statt nur das Vorhandensein einer Zeile zu bestaetigen. Genau diese vier Stellen wurden im `dkv`-Durchlauf uebersehen; eine Pruefung, die eine stehen gebliebene Fassung bestehen liesse, waere schlimmer als keine, weil sie Sicherheit vortaeuscht."
- "789 Tests und die Typpruefung sind gruen, das Wegwerf-Werkzeug meldet alle Pruefungen bestanden; Schema, Migrationen, alle vier Compose-Dateien und beide Beispiel-Umgebungsdateien sind unveraendert; `DATABASE_URL` zeigt weiterhin auf die Rolle `tessera`, der Schalter bleibt AUS."
artifacts:
- apps/api/scripts/rls-scratch-check.mjs
@@ -893,8 +893,22 @@ Stellen, die im `dkv`-Durchlauf uebersehen wurden, und sie sind alle handgepfleg
- Die Bereichszeile `user` der Uebersichtstabelle mit den bei der Ausfuehrung NEU
gemessenen Zahlen fortschreiben, im Stil der bereits fortgeschriebenen Zeilen
- (`war X/0` plus eine Begruendung, welche Zugriffe umgestellt wurden und welche
- bewusst nicht). Die Summenzeile mitziehen.
+ (`war 17/0` plus eine Begruendung, welche Zugriffe umgestellt wurden und welche
+ bewusst nicht). Die Zahlen werden mit den beiden Befehlen ermittelt, die das
+ Dokument selbst ueber dieser Tabelle nennt — der eine zaehlt die ungebundenen
+ Rohtreffer, der andere die ueber den Namen `tenantPrisma` gebundenen. Die
+ Pruefung dieser Aufgabe ermittelt sie erneut und vergleicht sie gegen die Zeile:
+ das Dokument wird also an den Code angeglichen, nicht umgekehrt. Zur
+ Planungszeit an vier bereits umgestellten Bereichen gegengeprueft, dass diese
+ beiden Befehle deren Tabellenzeilen exakt reproduzieren. Daraus folgt eine
+ Auflage fuer Aufgabe 2 und 3, die sonst still unterlaufen wuerde: der gebundene
+ Klient heisst in jeder Methode `tenantPrisma`, wie in `ldap`, `groups`, `dkv` und
+ `auth` — ein anderer Name wuerde die zweite Zaehlung untertreiben lassen und die
+ Zeile ihrer Aussage berauben.
+- Die Summenzeile mitziehen. Die Pruefung dieser Aufgabe addiert die zwoelf
+ Bereichszeilen und haelt das Ergebnis gegen die Summenzeile — eine
+ fortgeschriebene Bereichszeile ueber einer stehen gebliebenen Summe faellt damit
+ durch, genau der Fehler, der im `dkv`-Durchlauf durchrutschte.
- Die vier vorhandenen Bestandsaufnahme-Zeilen des Bereichs auf den maschinell
gemessenen Stand setzen und dabei ZWEI Klassenkorrekturen vornehmen, jede mit
Begruendung: `user.service.ts`/`user` wechselt wegen der einen bewusst
@@ -911,7 +925,15 @@ Stellen, die im `dkv`-Durchlauf uebersehen wurden, und sie sind alle handgepfleg
wird `rls-access-inventory.spec.ts` rot (Befund N).
- Die Klassen-Verteilungstabelle samt ihrer Summe und der Ueberschrift, die die
Paarzahl nennt, auf die neuen Zahlen ziehen. Die Zahlen kommen aus der Ausgabe der
- Inventarpruefung, nicht aus einer Rechnung im Kopf.
+ Inventarpruefung, nicht aus einer Rechnung im Kopf. Die Pruefung dieser Aufgabe
+ zaehlt die Klassen ueber die Bestandsaufnahme-Zeilen selbst nach und haelt vier
+ Dinge gegeneinander: jede einzelne Klassenzahl, die Summe der Klassenzeilen, die
+ ausgewiesene Summenzeile und die Paarzahl in der Ueberschrift. Weil dieser
+ Durchlauf mit dem Schleifentreiber ein zusaetzliches Paar erzeugt, wandern alle
+ vier gemeinsam — eine stehen gebliebene faellt durch. Zur Planungszeit gegen das
+ heutige, in sich stimmige Dokument getestet (es besteht) und gegen eine
+ mutierte Fassung, in der genau dieser Fehler nachgestellt wurde (sie faellt
+ durch, mit der Meldung, welche Zahl klemmt).
- Den Abschnitt zum Hintergrunddienst als Falle um den fuenften Fall erweitern und
seine Ueberschrift, die heute vier Faelle nennt, mitziehen. Der fuenfte Fall ist die
Standardgruppen-Reparatur beim Start (Befund K) und ist ausdruecklich als der
@@ -934,9 +956,9 @@ urspruengliche Text NICHT umgeschrieben — er beschreibt korrekt den Zustand zu
Zeitpunkt der Umstellung; der Nachtrag steht daneben.
- DB_IP=$(docker inspect tessera-ctl-db-1 --format '{{range $k,$v := .NetworkSettings.Networks}}{{$v.IPAddress}}{{end}}') && TESSERA_SCRATCH_ADMIN_URL="postgresql://tessera:tessera_dev@${DB_IP}:5432/postgres" node apps/api/scripts/rls-scratch-check.mjs && npm --prefix apps/api run test && npm --prefix apps/api run type-check && npm --prefix apps/api run test -- src/prisma/rls-access-inventory.spec.ts && test -f apps/api/src/user/user.controller.spec.ts && grep -q '__makeBoundClient' apps/api/src/user/user.controller.spec.ts && grep -qE '^\| apps/api/src/user/user\.controller\.ts \| user \| muss-mandantengebunden \| gebunden \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/user\.service\.ts \| user \| beides \| gemischt \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/user\.service\.ts \| tenant \| keine-mandantengebundene-tabelle \| ungebunden \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/admin-seed\.service\.ts \| user \| beides \| gemischt \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/admin-seed\.service\.ts \| tenant \| keine-mandantengebundene-tabelle \| ungebunden \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^## Der Hintergrunddienst als Falle — fünf Fälle$' docs/mandantentrennung-zugriffsklassifikation.md && test -z "$(git diff --name-only HEAD -- apps/api/prisma apps/api/src/auth apps/api/src/ldap docker-compose.yml docker-compose.dev.yml docker-compose.prod.yml docker-compose.ci.yml .env.example .env.prod.example)"
+ DB_IP=$(docker inspect tessera-ctl-db-1 --format '{{range $k,$v := .NetworkSettings.Networks}}{{$v.IPAddress}}{{end}}') && TESSERA_SCRATCH_ADMIN_URL="postgresql://tessera:tessera_dev@${DB_IP}:5432/postgres" node apps/api/scripts/rls-scratch-check.mjs && npm --prefix apps/api run test && npm --prefix apps/api run type-check && npm --prefix apps/api run test -- src/prisma/rls-access-inventory.spec.ts && test -f apps/api/src/user/user.controller.spec.ts && grep -q '__makeBoundClient' apps/api/src/user/user.controller.spec.ts && grep -qE '^\| apps/api/src/user/user\.controller\.ts \| user \| muss-mandantengebunden \| gebunden \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/user\.service\.ts \| user \| beides \| gemischt \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/user\.service\.ts \| tenant \| keine-mandantengebundene-tabelle \| ungebunden \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/admin-seed\.service\.ts \| user \| beides \| gemischt \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^\| apps/api/src/user/admin-seed\.service\.ts \| tenant \| keine-mandantengebundene-tabelle \| ungebunden \|' docs/mandantentrennung-zugriffsklassifikation.md && grep -qE '^## Der Hintergrunddienst als Falle — fünf Fälle$' docs/mandantentrennung-zugriffsklassifikation.md && U=$(grep -ro "this\.prisma\.[a-zA-Z]*" apps/api/src/user | grep -v spec | wc -l | tr -d ' ') && B=$(grep -ro "tenantPrisma\.[a-zA-Z]*\." apps/api/src/user | grep -v spec | wc -l | tr -d ' ') && { test "$U" -lt 17 || { echo "UEBERSICHTSZEILE: ungebundene Rohtreffer in apps/api/src/user sind $U, also nicht gesunken — es wurde nichts umgestellt"; exit 1; }; } && { test "$B" -gt 0 || { echo "UEBERSICHTSZEILE: gebundene Rohtreffer in apps/api/src/user sind $B"; exit 1; }; } && { grep -qE "^\| user \| ${U} \| ${B} \| \*\*war 17/0\*\*" docs/mandantentrennung-zugriffsklassifikation.md || { echo "UEBERSICHTSZEILE user nennt nicht die neu gemessenen Zahlen ${U}/${B} im etablierten Stil"; exit 1; }; } && awk -F'|' '$2 ~ /^ *[a-z][a-z-]* *$/ && $3 ~ /^ *[0-9]+ *$/ && $4 ~ /^ *[0-9]+ *$/ { su+=$3; sb+=$4; n++ } $2 ~ /^ *\*\*Summe\*\* *$/ && $4 !~ /^ *$/ { g3=$3; g4=$4; gsub(/[^0-9]/,"",g3); gsub(/[^0-9]/,"",g4); ru=g3+0; rb=g4+0; seen=1 } END { if (!seen || n+0 != 12) { print "UEBERSICHTSTABELLE nicht erkannt, Bereichszeilen: " n; exit 1 } if (su+0 != ru || sb+0 != rb) { print "SUMMENZEILE stimmt nicht: Bereichszeilen ergeben " su "/" sb ", Summenzeile nennt " ru "/" rb; exit 1 } }' docs/mandantentrennung-zugriffsklassifikation.md && awk -F'|' '$2 ~ /^ *apps\/api\/src\// { k=$4; gsub(/^ +| +$/,"",k); cls[k]++; pairs++ } $2 ~ /^ *(muss-mandantengebunden|keine-mandantengebundene-tabelle|beides|bewusst-uebergreifend) *$/ && $3 ~ /^ *[0-9]+ *$/ && $4 ~ /^ *$/ { k=$2; gsub(/^ +| +$/,"",k); v=$3; gsub(/[^0-9]/,"",v); tab[k]=v+0; tn++ } $2 ~ /^ *\*\*Summe\*\* *$/ && $4 ~ /^ *$/ { v=$3; gsub(/[^0-9]/,"",v); tsum=v+0; tseen=1 } /^## Klassen-Verteilung/ { h=$0; gsub(/[^0-9]/,"",h); hp=h+0; hseen=1 } END { if (tn+0 != 4 || !tseen || !hseen) { print "KLASSEN-VERTEILUNG nicht erkannt: Klassenzeilen " tn ", Summenzeile " tseen ", Ueberschrift " hseen; exit 1 } if (tsum != pairs+0) { print "KLASSEN-SUMME stimmt nicht: Bestandsaufnahme hat " pairs " Paare, Tabellensumme nennt " tsum; exit 1 } if (hp != pairs+0) { print "UEBERSCHRIFT der Klassen-Verteilung nennt " hp " Paare, Bestandsaufnahme hat " pairs; exit 1 } s=0; for (k in tab) { if (tab[k] != cls[k]+0) { print "KLASSE " k ": Tabelle nennt " tab[k] ", Bestandsaufnahme zaehlt " cls[k]+0; exit 1 } s+=tab[k] } if (s != pairs+0) { print "KLASSENZEILEN ergeben " s ", Bestandsaufnahme hat " pairs; exit 1 } }' docs/mandantentrennung-zugriffsklassifikation.md && test -z "$(git diff --name-only HEAD -- apps/api/prisma apps/api/src/auth apps/api/src/ldap docker-compose.yml docker-compose.dev.yml docker-compose.prod.yml docker-compose.ci.yml .env.example .env.prod.example)"
- Alle sieben Zugriffe der Steuerungsschicht laufen ueber `forTenant()` beziehungsweise ueber die uebergreifenden Methoden des Dienstes, deren Rumpf je Mandant gebunden ist; die Rollenverzweigung zwischen mandantengebundener und uebergreifender Sicht ist erhalten und durch Tests belegt; der Riegel gegen das Loeschen des eigenen Kontos greift, und sein Rotwerden vor der Reparatur ist im SUMMARY festgehalten; `apps/api/src/user/user.controller.spec.ts` existiert, nutzt den Zwei-Klienten-Nachweis und deckt die acht in `` genannten Faelle ab; der Testlauf ist gruen mit mehr als 789 Tests und die Typpruefung sauber; `rls-access-inventory.spec.ts` laeuft gruen und stimmt mit den fuenf Bestandsaufnahme-Zeilen des Bereichs ueberein, einschliesslich der neu ergaenzten Zeile fuer den Schleifentreiber; die beiden Klassenkorrekturen sind mit Begruendung vollzogen; Uebersichtszeile, Summenzeile, Klassen-Verteilungstabelle samt Summe und Ueberschrift sowie der Abschnitt zur Hintergrunddienst-Falle samt seiner Ueberschrift sind von Hand nachgezogen; die Kritikschrift traegt den Nachtrag mit den tatsaechlich umgesetzten Pfaden; das Wegwerf-Werkzeug meldet alle Pruefungen bestanden; `auth.service.ts`, `ldap.service.ts`, Schema, Migrationen, Compose- und Beispiel-Umgebungsdateien sind unveraendert und `DATABASE_URL` zeigt weiterhin auf die Rolle `tessera`.
+ Alle sieben Zugriffe der Steuerungsschicht laufen ueber `forTenant()` beziehungsweise ueber die uebergreifenden Methoden des Dienstes, deren Rumpf je Mandant gebunden ist; die Rollenverzweigung zwischen mandantengebundener und uebergreifender Sicht ist erhalten und durch Tests belegt; der Riegel gegen das Loeschen des eigenen Kontos greift, und sein Rotwerden vor der Reparatur ist im SUMMARY festgehalten; `apps/api/src/user/user.controller.spec.ts` existiert, nutzt den Zwei-Klienten-Nachweis und deckt die acht in `` genannten Faelle ab; der Testlauf ist gruen mit mehr als 789 Tests und die Typpruefung sauber; `rls-access-inventory.spec.ts` laeuft gruen und stimmt mit den fuenf Bestandsaufnahme-Zeilen des Bereichs ueberein, einschliesslich der neu ergaenzten Zeile fuer den Schleifentreiber; die beiden Klassenkorrekturen sind mit Begruendung vollzogen; alle VIER handgepflegten Stellen sind nachgezogen UND einzeln maschinell gegatet — die Uebersichtszeile gegen die am Code neu ermittelten Rohtrefferzahlen, die Summenzeile gegen die Addition der zwoelf Bereichszeilen, die Klassen-Verteilung gegen die ueber die Bestandsaufnahme nachgezaehlten Klassen samt Summe und Ueberschrift, und der Abschnitt zur Hintergrunddienst-Falle gegen seine byte-genaue Ueberschrift; keine dieser vier Pruefungen kann von einer stehen gebliebenen Fassung bestanden werden; die Kritikschrift traegt den Nachtrag mit den tatsaechlich umgesetzten Pfaden; das Wegwerf-Werkzeug meldet alle Pruefungen bestanden; `auth.service.ts`, `ldap.service.ts`, Schema, Migrationen, Compose- und Beispiel-Umgebungsdateien sind unveraendert und `DATABASE_URL` zeigt weiterhin auf die Rolle `tessera`.