- auth.controller.spec.ts: NEU. Mandantenquelle je Handler (das Claim fuer
me/changePassword; fuer die oberste Rolle der Mandant des Ziels aus dem
Fan-out), unbekanntes Ziel, null-Durchreichung von me, Rollen-Metadaten
(ROLES_KEY) und Public-Metadaten (IS_PUBLIC_KEY) fuer alle sieben
Handler — 23 Faelle; Falsifizierungsnachweis am Fan-out-Zweig
durchgefuehrt und zurueckgenommen
- docs/mandantentrennung-zugriffsklassifikation.md: Uebersichtszeile auth
auf 3/10 (war 8/5), Summenzeile 78/167, Bestandsaufnahme-Zeile
auth.service.ts/user auf gebunden (keine gemischt-Zeile mehr fuer diese
Datei), Klassen-Verteilung unveraendert bei 64 Paaren mit
Stand-Vermerk, Hintergrunddienst-Abschnitt ohne sechsten Fall,
Etappe-3-Anmeldeweg-Punkt in "Was diese Etappe NICHT entscheidet";
beide Dokument-Falsifizierungen durchgefuehrt und zurueckgenommen
- .planning/WINDOWS.md: zwei neue offene Eintraege (#28 verschluckte Leere
im Frontend, Familie #23/#25/#26; #29 Rechteausweitung ADMIN->SUPER_ADMIN
im Schwesterweg PATCH /users/:id, T-FH9-05)
Baseline wiederhergestellt: 951/951 Tests gruen in 60 Dateien (927+23
neue Faelle plus der in Aufgabe 2 erwartungsgemaess rote Test), Werkzeug
120/120, Typpruefung sauber.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- auth.service.ts: die drei Nach-Anmeldungs-Methoden binden je ueber genau
einen Klienten tenantPrisma; adminResetPassword verweigert einem
Nicht-SUPER_ADMIN das Kennwort eines SUPER_ADMIN (T-FH9-04); die drei
$queryRaw-Anmeldesuchen bleiben unveraendert auf dem ungebundenen Klienten
- auth.controller.ts: me/changePassword reichen user.tenantId aus dem Claim
durch; adminResetPassword verzweigt ueber resolveTargetTenantId nach Rolle
(ADMIN: eigener Mandant; SUPER_ADMIN: gebundener Fan-out
UserService.findByIdForPlatformAdmin) — schliesst die Rechteausweitung
ueber die Mandantengrenze (T-FH9-01)
- auth.module.ts: importiert UserModule, zyklusfrei gemessen
- auth.service.spec.ts: Identitaets-Attrappe ersetzt durch zwei
unterscheidbare Klienten (__makeBoundClient); 29 Faelle, drei
Falsifizierungsnachweise durchgefuehrt und zurueckgenommen
Bekannt und erwartet: rls-access-inventory.spec.ts ist nach diesem Commit
kurzzeitig rot (Bestandsaufnahme-Zeile auth.service.ts/user zeigt noch
"gemischt", gemessen ist jetzt "gebunden") — wird in Aufgabe 3 desselben
Plans geschlossen (927/928 Tests gruen, ein bekannter, in Aufgabe 3
behobener Fehlschlag, kein neuer).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- rls-scratch-check.mjs: dreizehnter Abschnitt runAuthAreaChecks, getrennt
von runAuthLookupChecks — misst die Grenze zwischen Anmeldeweg (drei
SECURITY-DEFINER-Funktionen, unveraendert) und Nach-Anmeldung (getMe,
changePassword, adminResetPassword) ueber den generierten Client; neuer
Helfer readSchemaModelScalarFieldNames() filtert Relationsfelder
ueber ihren Typ heraus
- zehn neue, namentlich benannte Pruefungen (120/120 insgesamt), sechs davon
ueber den generierten Client an der auf 15 Spalten erweiterten
Wegwerf-Tabelle "User"; pg_proc bestaetigt SECURITY DEFINER/STABLE/festen
Suchpfad/LIMIT 1 fuer alle drei Anmeldefunktionen
- docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt
"## Bereich auth" (h1)-(h5) vor "## Verweis" — Signaltabelle je Pfad,
getMe-Kette als "verschluckt" statt "laut", Etappe-3-Vorbehalt,
Schwesterweg-Luecke in user.controller.ts
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
findAll/findOne/remove zaehlen Benutzer je Mandant jetzt ueber drei
gebundene Aufrufstellen (tenantPrisma.user.count mit where: { tenantId
}, in remove zusaetzlich isActive: true) statt ueber den
Relationszaehler, der nach dem Scharfschalten unbemerkt unter der
Regel von User gelaufen waere (260911-e2s, Aufgabe 1, Pruefungen 5-7).
Fan-out-Muster aus UserService.findAllForPlatformAdmin uebernommen; die
vier tenant-Zugriffe bleiben ungebunden (Tenant ohne Regel). Antwortform,
Meldungen und Statuscodes unveraendert.
tenant.controller.spec.ts legt die Testlage aus dem Nichts an (20
Faelle): Zwei-Klienten-Nachweis ueber __makeBoundClient, Rollen-
Metadaten-Test (Klasse SUPER_ADMIN, kein Handler ueberschreibt), Wachhund
gegen mehrfache Klientenerzeugung. Falsifizierungsnachweis durchgefuehrt:
der probeweise ungebundene Zaehler in findOne macht 2 Faelle rot mit
"Cannot read properties of undefined (reading 'count')" — die dkv-Form
der Falsifizierung, nicht nur eine falsche Zahl —, danach zurueckgenommen.
Klassifikation und Entwicklungsanleitung nachgezogen: 64 Paare (ein
neues, tenant.controller.ts/user), Uebersichtszeile 8/3, Klassen-
Verteilung 32 muss-mandantengebunden, Erkennungsluecke fuer
Relationseinbindungen im Kopf der Bestandsaufnahme benannt, "Zwei
belegte Befunde" und "Was diese Etappe NICHT entscheidet" (erster
Punkt aufgeloest). Beide Dokument-Falsifizierungsnachweise durchgefuehrt
(falsche Klasse macht rls-access-inventory.spec.ts rot, falsche
Uebersichtszahl macht das herleitende Gate rot), zurueckgenommen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
Die vorige Aufgabe-2-Teilcommit (11f5731) hatte nur die Loeschung von
tenant.middleware.ts und die neue tenant.guard.spec.ts erfasst — ein
`git add` mit mehreren Pfaden schlug wegen eines bereits entfernten
Pfads fataler fehl und liess die restlichen fuenf Dateien unstaged,
ohne dass das beim Commit auffiel (Rule 1 — Prozessfehler, hier
korrigiert). Dieser Commit traegt den eigentlichen Umbau nach:
tenant.guard.ts ohne Prisma-Abhaengigkeit, die geleerte
FORTENANT_ASSIGNMENT_EXCEPTIONS samt Wachhund-Test in
rls-access-inventory.spec.ts, und die drei berichtigten
Kommentarzeilen (app.module.ts, module.guard.ts, dkv.controller.ts).
Inhaltlich identisch mit dem, was bereits verifiziert wurde (891 Tests
gruen, Typpruefung sauber) — nur die Staging-Reihenfolge war fehlerhaft.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
Die seit Etappe 1 offene Architekturfrage zum gebundenen Klienten auf
dem Anfrageobjekt ist entschieden: neun umgestellte Bereiche binden
ausnahmslos dienst-intern (ein Klient je Methode), ein Klient auf
req.tenantPrisma ohne Leser war tote Verdrahtung, die wie ein
Sicherheitsmechanismus aussah. tenant.guard.ts verliert die
Prisma-Abhaengigkeit und setzt nur noch req.tenantId; die nie
verdrahtete tenant.middleware.ts (identische Logik, in keinem Modul
registriert) ist geloescht.
tenant.guard.spec.ts legt die Testlage aus dem Nichts an — alle fuenf
Zweige (kein Nutzer, USER, ADMIN mit ignorierter x-tenant-id-Kopfzeile
T-04-03, SUPER_ADMIN mit/ohne Wechsel, mandantenloser Nicht-SUPER_ADMIN)
sowie die Abwesenheit der alten Eigenschaft in jedem Durchlass-Fall.
Falsifizierungsnachweis durchgefuehrt: das probeweise Wiedereinfuehren
der alten Zuweisung macht 4 der 7 Faelle rot (u. a. "expected true to
be false" auf 'tenantPrisma' in req), danach zurueckgenommen.
rls-access-inventory.spec.ts: FORTENANT_ASSIGNMENT_EXCEPTIONS ist leer
und selbstpruefend (neuer Wachhund gegen veraltete Eintraege). Drei
Fremdkommentare (app.module.ts, module.guard.ts, dkv.controller.ts)
korrigiert, die noch auf die nie verdrahtete Middleware verwiesen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
runTenantAreaChecks (9 neue Pruefungen, 6 davon ueber den generierten
Client) belegt: auf "Tenant" ist nichts zu binden (keine Regel in allen
34 Migrationen einschliesslich 20260910120000), aber der
Relationszaehler in findAll/findOne/remove liefert nach dem
Scharfschalten userCount=0 fuer jeden Mandanten und laesst den
Loeschriegel T-02-09 vakuum werden — der Fremdschluessel faengt das
nur laut (500) statt mit der verstaendlichen 400-Meldung ab.
docs/mandantentrennung-etappe2-fehlerrichtung.md bekommt den Abschnitt
"## Bereich tenant" (n1-n5) mit der tatsaechlich beobachteten
Werkzeugausgabe, der Signaltabelle je Pfad, den Frontend-Stellen, die
die falsche Zahl unkommentiert durchlassen, und der Entscheidung zur
Anfrageobjekt-Eigenschaft (Vorbereitung fuer Aufgabe 2).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- docs/mandantentrennung-zugriffsklassifikation.md: Uebersichtszeile
calendar auf 0/12 gezogen (war 12/0, kein ungebundener Rest — erster
Bereich in Folge ohne begruendeten Rest), Summenzeile auf 83/159
fortgeschrieben, Klassen-Verteilung unveraendert mit ausdruecklichem
Stand-260911-cwh-Vermerk, Hintergrunddienst-Abschnitt um den
Sonderfall refreshCacheInBackground (abgekoppelte Fortsetzung, kein
sechster Fall) ergaenzt, Abschnitt "Was diese Etappe NICHT entscheidet"
um calendar ergaenzt
- .planning/WINDOWS.md: neuer offener Eintrag (#26) zur lautlosen
Auspraegung der umgekehrten Fehlerrichtung im Bereich calendar, ueber
gsd-tools windows append angelegt
- Beide Falsifizierungsnachweise durchgefuehrt (falscher Stand macht
rls-access-inventory.spec.ts rot; falsche Uebersichtszahl macht das
herleitende Gate rot), zurueckgenommen
- Etappe 2, neunter Bereich (calendar) abgeschlossen: alle zwoelf
Zugriffe gebunden, Baseline gehalten (883 Tests, 57 Dateien, 101
Werkzeugpruefungen, Typpruefung sauber)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- calendar.service.spec.ts: NEU, Zwei-Klienten-Nachweis ueber __makeBoundClient
(Muster dkv.service.spec.ts), Attrappen fuer CryptoService und die drei
Provider, 23 Testfaelle: getSources/addSource, updateSource inkl. drei
Erhaltungsfaelle, alle drei Besitzpruefungen je Ausnahmeart, testConnection
Erfolgs-/Fehlerpfad, aggregateEvents/fetchAndCacheEvents inkl. beider
Synchronstatus-Rueckschreibungen, Cache-Verhalten inkl. Nutzer-Trennung,
testConnectionFromConfig ohne DB-Zugriff, Wachhund fuer genau einen
gebundenen Klienten je Aufruf
- calendar.service.ts: alle 12 Zugriffe auf forTenant() umgestellt, ein
tenantPrisma-Klient je Methode (getSources, addSource, updateSource,
deleteSource, testConnection, fetchAndCacheEvents); Cache-Schluessel-Urteil
und die kein-sechster-Hintergrunddienst-Begruendung als Kommentare
festgehalten
- calendar.controller.ts: alle sechs kontextnutzenden Handler reichen
Benutzer- UND Mandantenkennung aus extractContext durch, keine neue
Vertrauensquelle
- Falsifizierungsnachweis durchgefuehrt: eine Rueckschreibung testweise
entbunden, benannter Test ging rot ("aggregateEvents, Erfolgspfad"), Fund
bestaetigt, zurueckgenommen
- docs/mandantentrennung-zugriffsklassifikation.md: Bestandsaufnahme-Zeile
calendar.service.ts/calendarSource auf gebunden gezogen
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- rls-scratch-check.mjs: elfter Abschnitt runCalendarAreaChecks mit 13
namentlich benannten Pruefungen gegen die aus 20260909140000_rls_remaining_tenant_tables
geschnittene Regel, davon 4 ueber den generierten Client an einer
schemagleichen Wegwerf-Tabelle (17 Spalten, gegen schema.prisma
laufzeitgeprueft); Laufzeitpruefung, dass 20260910120000 keine eigene
CalendarSource-Regel traegt (Messfalle 260910-jab)
- Alle 101 Pruefungen bestehen (88 bisherige + 13 neue)
- docs/mandantentrennung-etappe2-fehlerrichtung.md: Abschnitt
"## Bereich calendar" mit (k1)-(k5) — Messung, Signaltabelle je Pfad,
Leere-als-Abwesenheit in Backend UND Frontend samt Fehlerverschluckung,
bewusst nicht geloest (Cache-Schluessel-Urteil, Zugangsdaten-Erhaltung,
fehlende Benutzerdimension, 403/404), bewusst nicht angefasst
- Wettlauf-Fehlerklasse gemessen: PrismaClientKnownRequestError (P2025),
NICHT PrismaClientUnknownRequestError wie im Bereich dashboard — Aufgabe
2 braucht deshalb keine neue Fehleruebersetzung fuer die Besitzpruefungen
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
Aufgabe 3 — TDD zuerst (7 weitere Faelle in dashboard.service.spec.ts, 20
vorher/27 nach dieser Aufgabe), dann die Umstellung:
- getSearchProviders/addSearchProvider/removeSearchProvider laufen ueber
forTenant(); removeSearchProvider fuehrt Besitzpruefung UND Schreibzugriff
ueber DENSELBEN gebundenen Klienten. Die drei Vorgabe-Suchmaschinen aus
der Konstante bleiben unveraendert vorangestellt.
- Der eine Katalogzugriff (this.prisma.module in getWidgets) bleibt
begruendet ungebunden: Messung und Bedingung getrennt (Tabelle traegt
heute keinen Zeilenschutz, wirkungslos statt katastrophal — katastrophal
erst, wenn Etappe 3 eine Regel gibt), unter Berufung auf die bestehende
Werkzeugpruefung module-tabelle-traegt-keinen-zeilenschutz statt einer
neuen Behauptung. Ein Wachhund-Testfall haelt den Katalogzugriff aus dem
Bindungsprotokoll heraus (und beweist zuerst, dass der Katalogpfad
tatsaechlich durchlaufen wird, nicht nur theoretisch geprueft ist).
- docs/mandantentrennung-zugriffsklassifikation.md an allen fuenf
handgepflegten Stellen nachgezogen: vier Bestandsaufnahme-Zeilen (inkl.
eigenstaendiger Nachpruefung der widerlegten SearchProvider-Praemisse),
Uebersichtszeile (13/0 -> 1/12), Summenzeile (95/147), Klassen-Verteilung
(unveraendert 63 Paare, ausdruecklich vermerkt), Hintergrunddienst-
Abschnitt (dashboard hat keinen sechsten Fall, mit Messanweisung), "Was
diese Etappe NICHT entscheidet" (dienst-interner forTenant()-Weg wie alle
sieben Bereiche vor ihm).
- .planning/WINDOWS.md traegt Eintrag #25 (offen, Tabelle + JSON): die
beweisvernichtende Schleife (leeres Dashboard -> Neuaufbau ->
automatisches Zurueckschreiben -> ueberschriebene Anordnung, Widget-
Dubletten) samt der Vorabpruefung fuer Etappe 4 und dem Verweis auf #22
fuer die verwandte Eindeutigkeitsfrage.
Zwei weitere Falsifizierungsnachweise durchgefuehrt: (1) den Katalogzugriff
probeweise gebunden (tenantPrisma.module.findMany) — acht Tests werden rot
mit "TypeError: Cannot read properties of undefined (reading 'findMany')",
weil `module` bewusst nicht in der Testdouble-Bindungsliste steht; Rueckbau
zurueckgenommen, 27/27 wieder gruen. (2) den Stand von dashboardLayout in
der Klassifikationsdatei probeweise auf "ungebunden" gesetzt —
rls-access-inventory.spec.ts wird rot mit "Abweichender Stand (Dokument vs.
Quelltext): ... dokumentiert=ungebunden, gemessen=gebunden"; Ruecknahme,
Testlauf wieder gruen (10/10).
Baseline gehalten: 858 Tests / 56 Dateien gruen, Typpruefung sauber,
Wegwerf-Werkzeug 87/87. Schalter bleibt aus.
Aufgabe 2 — TDD zuerst (20 Faelle in dashboard.service.spec.ts, 8 vorher/12
neu, Zwei-Klienten-Nachweis ueber __makeBoundClient nach dem Muster von
module-access.service.spec.ts), dann die Umstellung:
- getLayout/saveLayout laufen GEMEINSAM gebunden (ein Testfall nagelt das
fest); saveLayout uebersetzt eine gebundene Konflikt-Schreibung
(PrismaClientUnknownRequestError, gemessen in Aufgabe 1 — NICHT P2002)
in eine deutsche ConflictException.
- getWidgets/addWidget/updateWidgetConfig/removeWidget laufen gebunden;
die drei Besitzpruefungen ueber die Benutzerkennung bleiben unveraendert
bestehen (die Regeln dieses Bereichs kennen keine Benutzerdimension).
updateWidgetConfig/removeWidget fuehren Besitzpruefung UND Schreibzugriff
ueber DENSELBEN gebundenen Klienten.
- dashboard.controller.ts reicht den bereits aufgeloesten Mandanten bei
getLayout/updateWidgetConfig/removeWidget durch (keine neue
Vertrauensquelle, weiterhin aus extractContext/Sitzungsnachweis).
- Der Modulkatalog und die vier Suchmaschinenzugriffe bleiben in dieser
Aufgabe unveraendert (Aufgabe 3).
- Falsifizierungsnachweis durchgefuehrt: tenantPrisma.widgetInstance.delete
probeweise auf this.prisma zurueckgebaut — Test "Widget entfernen: ebenso,
beide Abfragen ueber denselben Klienten" wird rot mit "erwarteter
gebundener Aufruf widgetInstance.delete(tenant=tenant-1) fehlt im
Protokoll"; Rueckbau zurueckgenommen, Testlauf wieder gruen (20/20).
Zwei dokumentierte Abweichungen (Rule 3): (1) Befund A hatte fuer
widgetInstance sieben Treffer vorhergesagt, gemessen sind sechs (macht
zusammen mit dashboardLayout acht statt neun) — der Verify-Schwellwert
wird entsprechend auf >=8 gelesen. (2) Die Stand-Spalte fuer
dashboardLayout/widgetInstance in der Klassifikationsdatei wird bereits
hier minimal nachgezogen (nicht erst in Aufgabe 3), weil
rls-access-inventory.spec.ts sonst am Ende dieser Aufgabe rot waere —
derselbe Praezedenzfall wie 260910-exd, Aufgabe 2.
Baseline gehalten: 851 Tests / 56 Dateien gruen (839 + 12 neue), Typpruefung
sauber, Wegwerf-Werkzeug 87/87.
Aufgabe 1 — misst die umgekehrte Fehlerrichtung des Bereichs dashboard an
den Regeln nach Migration 20260910120000, VOR der Umstellung:
- rls-scratch-check.mjs bekommt einen neunten Abschnitt
(runDashboardAreaChecks) mit 13 neuen, namentlich benannten Pruefungen
gegen die aus der ausgelieferten Migration geschnittenen Regeln fuer
DashboardLayout, WidgetInstance und SearchProvider. Alle 87 Pruefungen
bestehen (74 bisherige + 13 neue).
- Die Konfliktmessung (Befund K) ist gemessen, nicht angenommen: ein
gebundenes INSERT ... ON CONFLICT auf eine unter dem Mandanten
unsichtbare Zeile scheitert laut mit SQLSTATE 42501. Zusaetzlich am
echten generierten Prisma Client gemessen: prisma.dashboardLayout.upsert()
wirft PrismaClientUnknownRequestError (nicht P2002) — das tenders-Muster
laesst sich deshalb nicht woertlich uebernehmen.
- Die widerlegte Praemisse zu SearchProvider (WINDOWS #19) ist in diesem
Durchlauf eigenstaendig nachgeprueft, mit benannter Suchreichweite.
- docs/mandantentrennung-etappe2-fehlerrichtung.md bekommt den Abschnitt
"## Bereich dashboard" (w1)-(w5) samt der beweisvernichtenden Schleife
(leeres Dashboard -> Neuaufbau -> automatisches Zurueckschreiben ->
ueberschriebene Anordnung) und einen Nachtrag im Abschnitt
"## Bereich module-registry" zur geerbten Bindungsentlastung.
Baseline gehalten: 839 Tests / 56 Dateien gruen, Typpruefung sauber.
Schalter bleibt aus.
Dreizehn Zugriffe, alle in dashboard.service.ts: zwoelf werden gebunden,
einer (der plattformweite Modulkatalog) bleibt begruendet ungebunden.
Zur Planungszeit gemessen statt angenommen:
- Die Zahl 13 haelt beim Hineinschauen.
- Die Besitzpruefungen sind ECHT (Pruefung auf die Benutzerkennung nach
dem Laden), anders als die gleich geformte Luecke im Bereich ldap.
- Die widerlegte Praemisse zu SearchProvider haelt einer eigenstaendigen
Suche stand; die Reichweite der Suche ist benannt, damit sie
widerlegbar bleibt.
- Die Modul-Zugriffsaufloesung ist bereits gebunden — die geerbte
Entlastung wird nachgeprueft, nicht ein zweites Mal repariert.
- Alle drei Regeln kennen keine Benutzerdimension; die
anwendungsseitigen Pruefungen bleiben der einzige Quer-Lese-Schutz.
- Die beweisvernichtende Schleife ist an den beiden Web-Dateien belegt:
leeres Dashboard, Neuaufbau, automatisches Zurueckschreiben beim
Verlassen des Bearbeitungsmodus, ueberschriebene Aufzeichnung.
Umfang als Erlaubnisliste gegen den Ausgangsstand f205120 gegatet statt
als Verbotsliste; die Zaehlgates der Klassifikation leiten ihre Werte aus
den dokumenteigenen Messanweisungen ab.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- WINDOWS.md: #19 auf fixed gesetzt (Tabelle + JSON-Block), mit Beleg
(Migrationsname + benannte Pruefungen) und ausdruecklicher Feststellung,
dass die SearchProvider-Haelfte als widerlegte Praemisse schliesst, nicht
als geloestes Problem. #18/#20/#21/#22/#23 bleiben unveraendert offen. Ein
neuer Eintrag #24 haelt den fehlenden Verwaltungsweg fuer plattformweite
Zeilen unter der Anwendungsrolle offen (verschwindet nicht mit #19). Die
vier Kopfzahlen sind aus dem JSON-Block abgeleitet (6 offen, 17 behoben, 1
zurueckgestellt, 24 gesamt).
- Klassifikation: #19-Block von offener Frage zu beantwortet, die
Uebersichtszeile tenders (35/27) und Summenzeile (107/135) aus dem
Quelltext neu abgeleitet, vier Bestandsaufnahme-Zeilen nachgezogen
(searchProvider, groups.service.ts/user, module-grants.service.ts/
moduleGrant, tender-rss-feed.service.ts/tenderRssFeedSource), der Punkt in
"Was diese Etappe NICHT entscheidet" aufgeloest.
- Kritikschrift: neuer Abschnitt "Regelschluss T-JTS-02, T-JTS-03 und
WINDOWS #19" mit tatsaechlich beobachteter Ausgabe, Regelliste der
lebenden Datenbank, Signaltabelle mit beiden Fehlerrichtungen je Regel,
der neuen Stelle aus Befund F, der selbst ausgefuehrten Messung zur
fehlenden Benutzerdimension (keine zweite Sitzungsvariable gefunden) und
dem, was dieser Durchlauf nicht loest/nicht anfasst. Fuenf ueberholte
Bestandsstellen mit Nachtraegen versehen (g4, t4, die Signaltabellenzeile
zu den RSS-Pfaden, drei aufgezeichnete Werkzeugausgaben, m4), die alten
Messprotokolle bleiben woertlich stehen.
- Betriebsanleitung: die eine Stelle, die #19 als offen fuehrte, nennt jetzt
den Aufloesungsstand und WINDOWS #24.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- TenderRssFeedSourceService.listForUser() nimmt jetzt (userId, tenantId)
entgegen und laeuft ueber einen gebundenen Klienten (forTenant) — die neue
Leseregel schliesst plattformweite Zeilen ein, die Reparatur haette den
ungebundenen Pfad sonst still auf nur die plattformweiten Zeilen reduziert
(Befund F). createPlatform/remove bleiben bewusst ungebunden, Kommentare an
der neuen Regel richtiggestellt.
- TendersController.listRssFeeds reicht die Mandantenkennung aus dem
Aufrufzusammenhang durch.
- Vier Aufzeichnungen im Quelltext (module-access.service.ts,
groups.service.ts, module-grants.service.ts, rls-coverage.spec.ts) sagen
jetzt, dass die Datenbankregel seit 20260910120000_rls_widen_membership_
grant_and_platform_read beide Seiten prueft; die Anwendungspruefungen
bleiben unveraendert bestehen (zweites Netz, wirkt vor dem Scharfschalten
als einziger Schutz).
- Zwei-Klienten-Nachweis in module-grants.service.spec.ts ergaenzt (Kommentar,
warum die beiden Cross-Tenant-Tests nach der Regelaenderung nicht entfallen
duerfen) und in tender-rss-feed.service.spec.ts umgekehrt (listForUser
bindet jetzt).
- Rule 1: implizites any beim Destrukturieren in listRssFeeds (feeds ist seit
der Bindung `any`) mit expliziter Annotation behoben.
- Falsifizierungsnachweis durchgefuehrt: Bindungsaufruf zurueckgenommen,
genau ein Test wurde rot (AssertionError, 0 statt der erwarteten
Aufrufe), Ruecknahme rueckgaengig gemacht.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- Neue, handgeschriebene Migration 20260910120000_rls_widen_membership_grant_and_platform_read:
GroupMembership prueft jetzt beide Seiten der Beziehung (Gruppe UND Benutzer),
ModuleGrant prueft zusaetzlich die referenzierte Gruppe/den referenzierten Benutzer
(mit Leer-Zulassung, D-04), TenderRssFeedSource bekommt vier nach Befehl getrennte
Regeln statt einer (Lesen schliesst plattformweite Zeilen ein, Schreiben verlangt
weiterhin einen Mandanten). SearchProvider bewusst unveraendert (Befund E: Praemisse
widerlegt). Lokal angewandt und gegen den Systemkatalog der lebenden Datenbank
gemessen. Der Schalter bleibt aus (Rolle tessera).
- rls-scratch-check.mjs: die drei loch-behauptenden Pruefungen umgekehrt (nicht
geloescht), Gegenmessungen ueber die Wartungsrolle ergaenzt, vier Befehlsrichtungen
fuer TenderRssFeedSource gemessen, neuer Abschnitt fuer SearchProvider, Extraktion
auf die neue Migration umgeleitet und um eine mehrfach-treffer-faehige Form ergaenzt
(extractAllPolicySql).
- migration-sql.spec.ts: neuer Beschreibungsblock fuer die neue Migration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
Schliesst T-JTS-02, T-JTS-03 und WINDOWS #19 in einer handgeschriebenen
Migration. Drei zur Planungszeit gemessene Funde praegen den Zuschnitt:
eine DRITTE loch-behauptende Pruefung im Wegwerf-Werkzeug (nicht zwei), eine
Zeile, auf der zwei spaetere Pruefungen aufsetzen und die nach der Reparatur
nicht mehr entsteht, und genau ein Anwendungspfad, den die Reparatur still
falsch machen wuerde.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- module-registry.service.ts: findActiveForTenant, activateForTenant,
isModuleActive binden je einen Aktivierungszugriff, deactivateForTenant
bindet beide (Lesen+Schreiben) ueber EINEN Klienten unter tenantPrisma;
alle sechs Katalogzugriffe (findAll/findBySlug/beide
Existenzpruefungen/isModuleActive-Katalogsuche/seedModule) bleiben
bewusst ungebunden, mit Kommentar der Messung von Bedingung trennt
- isModuleActive-Kopfkommentar richtiggestellt: der Waechter ruft sie
nicht auf (0 Aufrufer, TEIL 3 von Aufgabe 1) — Waechter nimmt findBySlug
+ ModuleAccessService.getAccessibleModuleIds
- module-registry.service.spec.ts: NEU, Zwei-Klienten-Nachweis, deckt die
bislang ungetestete Datei mit elf der siebzehn Zugriffe des Bereichs ab,
inkl. der lauten (deactivate ohne Aktivierung) und stillen (isModuleActive
ohne Aktivierung) Richtung und dem Katalog-Wachhund
- tender-scheduler.service.spec.ts: forTenant() auf Identitaet gemockt
(dieselbe Konvention wie ldap.service.spec.ts) — cross-area Bruch durch
die Umstellung von activateForTenant behoben (Rule 1/3)
- docs/mandantentrennung-zugriffsklassifikation.md: alle fuenf
handgepflegten Stellen nachgezogen (Bestandsaufnahme, Uebersichtszeile
7/10, Summenzeile 108/134, Klassen-Verteilung unveraendert bei 63 Paaren,
Hintergrunddienst-Abschnitt haelt die Abwesenheit eines sechsten Falls
fest) — alle gemessen, nicht abgeschrieben, Befund K haelt exakt
- docs/mandantentrennung-etappe2-fehlerrichtung.md: Nachtrag mit
tatsaechlich umgesetzten Pfaden, beiden Falsifizierungsnachweisen
(Testname+Meldung), und der Feststellung zum unveraenderten
Controller-Kommentar
- .planning/WINDOWS.md: neuer offener Eintrag #23 (deviation) — kein Signal
unterscheidet "keine Freigabe" von "Abfrage fand nichts", mit
Vorabpruefung fuer Etappe 4 und begruendeter Verwerfung einer
Laufzeitwarnung
- 833 Tests gruen (56 Dateien), Typpruefung sauber, Wegwerf-Werkzeug 66/66
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- module-access.service.ts: getAccessibleModuleIds (Kurzschlusszweig,
Direktweg, Gruppenweg, Schnittmenge) und getCatalogFlags' eigener
Aktivierungs-Lesezugriff laufen ueber forTenant(), EIN Klient je Methode
unter dem Namen tenantPrisma; der Katalogzugriff in findAccessibleModules
bleibt bewusst ungebunden (Modulkatalog traegt keine Regel), mit Kommentar
der Messung und Bedingung trennt
- Bestehende where-Filter mit tenantId bleiben als zweites Netz stehen
- module-access.service.spec.ts: Zwei-Klienten-Nachweis ueber
__makeBoundClient (Muster aus module-grants.service.spec.ts), alle 15
bestehenden Faelle erhalten, neue Faelle fuer jede in <behavior> genannte
Bindungseigenschaft inkl. Wachhund gegen eine kuenftige Katalogbindung
- module.guard.spec.ts: ein Fall, der die Abwesenheit eines
unterscheidenden Signals fuer "keine Freigabe" vs. "Abfrage fand nichts"
festnagelt
- Falsifizierungsnachweis durchgefuehrt: Gruppenweg-Bindung probeweise
zurueckgebaut, Test "USER-Zweig bindet BEIDE Freigabe-Lesezugriffe..."
wurde rot ("expected 1 to be 2"), Ruecknahme bestaetigt wieder gruen
- mandantentrennung-zugriffsklassifikation.md: Stand fuer
module-access.service.ts/moduleGrant und /tenantModuleActivation auf
gebunden nachgezogen (Rule 3 — sonst waere rls-access-inventory.spec.ts
rot geblieben); die uebrigen vier Bestandsaufnahme-Stellen bleiben
Aufgabe 3 vorbehalten
- 817 Tests gruen (55 Dateien), Typpruefung sauber, Wegwerf-Werkzeug 66/66
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- rls-scratch-check.mjs: achter Abschnitt runModuleRegistryAreaChecks mit 13
benannten Pruefungen gegen die echten, aus den ausgelieferten Migrationen
geschnittenen Regeln (Group/GroupMembership/ModuleGrant/TenantModuleActivation),
neu angelegt nur: Modulkatalog-Tabelle ohne Zeilenschutz, Eindeutigkeitsindex
auf TenantModuleActivation, zwei Direkt-Freigaben, eine fremde Mitgliedschaft
- Alle 66 Pruefungen bestanden (53 bisherige + 13 neue), 810 Tests gruen,
Typpruefung sauber
- docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt
"Bereich module-registry" mit den fuenf Unterabschnitten (m1-m5), inklusive
Praezisierung aus Befund E (Katalogbindung ist HEUTE wirkungslos, nicht
katastrophal — die Bedingung wird als Bedingung notiert) und der
unbeschoenigten Antwort auf die Signalfrage ("keines")
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- user.controller.ts: alle sieben Zugriffe binden. ADMIN-Zweig der
Benutzerliste laeuft ueber forTenant() mit weiterhin bestehender
Mandantenbedingung im where; SUPER_ADMIN-Zweig ueber die neue
UserService.findAllForPlatformAdmin(). Die drei Wege ueber die Kennung
loesen den Zielbenutzer rollenabhaengig ueber resolveTargetUser() auf
(ADMIN gebunden an eigenen Mandanten, SUPER_ADMIN uebergreifend); der
Schreibzugriff bei update/delete bindet an den Mandanten des
Zielbenutzers, nicht des Aufrufers, damit die uebergreifende
Verwaltung durch die oberste Rolle erhalten bleibt
- Selbstloesch-Riegel (Befund H) repariert: verglich bisher gegen
currentUser.sub, ein Feld, das der Sitzungsnachweis nicht traegt --
der Riegel griff nie. Jetzt gegen currentUser.id. Verhaltensaenderung:
ein Administrator kann sein eigenes Konto nun nicht mehr loeschen
- Alle fuenf Selbstbedienungszugriffe (Bild hochladen/loeschen/
ausliefern, Akzentfarbe) binden an die Mandantenkennung aus dem
Sitzungsnachweis
- user.controller.spec.ts (neu): Zwei-Klienten-Nachweis fuer die
vorher testlose Steuerungsschicht, 8 Testfaelle, Falsifizierungsnachweis
fuer eine gebundene Stelle sowie Rot-vor-Reparatur-Nachweis fuer den
Selbstloesch-Riegel (siehe SUMMARY)
- docs/mandantentrennung-zugriffsklassifikation.md: alle vier
handgepflegten Stellen nachgezogen (Uebersichtszeile 8/14, Summenzeile
118/124, Klassen-Verteilung 63 Paare, Hintergrunddienst-Abschnitt auf
fuenf Faelle inkl. admin-seed.service.ts als erster beidseitig
korrekter Fall) sowie zwei Klassenkorrekturen (user.service.ts/user
und admin-seed.service.ts/user je auf "beides")
- docs/mandantentrennung-etappe2-fehlerrichtung.md: Nachtrag zum
user-Abschnitt mit den tatsaechlich umgesetzten Pfaden, der
geschlossenen Luecke und den Falsifizierungsnachweisen
- 810 Tests gruen (8 neue in user.controller.spec.ts), Typpruefung
sauber, Wegwerf-Werkzeug meldet weiterhin alle 53 Pruefungen bestanden
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- user.service.ts: findById/update/deactivate/delete bekommen einen
Pflicht-Mandanten und laufen ueber forTenant(); create/update
uebersetzen die plattformweite Eindeutigkeitsverletzung (P2002) in eine
deutsche Konfliktmeldung ohne Halter/Mandant zu nennen; zwei neue
Methoden (findAllForPlatformAdmin, findByIdForPlatformAdmin) bilden die
Plattform-Administratorsicht als Schleife ueber alle Mandanten mit je
einem gebundenen Lesezugriff nach; findByUsername bleibt bewusst
ungebunden, Kopfkommentar richtiggestellt (Anmeldeweg laeuft seit
Etappe 1 ueber SECURITY-DEFINER-Funktionen, kein Aufrufer mehr)
- admin-seed.service.ts: Erstanlage-Pruefung bleibt ungebunden (mit
Begruendung), Erstanlage des Administrators bindet an den unmittelbar
zuvor angelegten Mandanten (Befund J-Korrektur); P2002 bei der Anlage
wird wie "Administrator existiert bereits" behandelt statt den Start
abzubrechen -- jeder andere Fehler bricht weiterhin ab
- user.controller.ts: die vier Aufrufstellen der geaenderten Signaturen
auf currentUser.tenantId umgestellt (Signatur-Minimalanpassung; die
Rollenlogik inkl. Plattform-Administratorsicht folgt in Aufgabe 3)
- Zwei-Klienten-Nachweis in beiden Testdateien (Muster
groups.service.spec.ts), Falsifizierungsnachweis fuer beide Bereiche
durchgefuehrt und zurueckgenommen (siehe SUMMARY)
- docs/mandantentrennung-zugriffsklassifikation.md: Zwischenstand fuer
(user.service.ts, user) und (admin-seed.service.ts, user) auf gemischt
korrigiert, neue Zeile (user.service.ts, tenant) ergaenzt -- volle
Klassenkorrektur mit Begruendung sowie die vier handgepflegten
Uebersichtstabellen folgen in Aufgabe 3
- .planning/WINDOWS.md: offener Eintrag fuer die plattformweite
Eindeutigkeit von username/email (Produktentscheidung fuer Etappe 3)
- 802 Tests gruen (13 neue in user.service.spec.ts, 4 neue in
admin-seed.service.spec.ts), Typpruefung sauber, Wegwerf-Werkzeug
meldet weiterhin alle 53 Pruefungen bestanden
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- runUserAreaChecks in rls-scratch-check.mjs: 12 neue Pruefungen gegen die
ausgelieferte User-Policy (baut auf der vom Anmeldeweg-Abschnitt
angelegten Tabelle auf, legt zusaetzlich Tenant ohne Zeilenschutz an)
- Belegt: ungebundene Suche nach vorhandenem Benutzernamen liefert 0
Zeilen, gebundene Suche nach fremdem Benutzernamen ebenso ("frei"), und
das anschliessende gebundene INSERT scheitert hart an SQLSTATE 23505
(Eindeutigkeitsverletzung), nicht an 42501 (Zeilenschutz)
- SQLSTATE wird aus err.meta.code gelesen, nicht err.code (das bei
$executeRaw-Fehlern immer den generischen Prisma-Code P2010 traegt,
empirisch gegen tessera-ctl-db-1 geprueft)
- docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt
"Bereich user" (u1-u5) mit der tatsaechlich beobachteten Ausgabe,
Signaltabelle, der vollstaendigen Kette (Befund L) und den Grenzen zu
auth.service.ts/ldap.service.ts
- Teil 2/3 gemessen: keine Transaktion in apps/api/src/user (Befund B
haelt), findByUsername hat genau einen Treffer, die eigene Definition
(Befund D haelt)
- 789 Tests weiterhin gruen, Typpruefung sauber, Wegwerf-Werkzeug meldet
alle 53 Pruefungen bestanden (41 bisherige + 12 neue)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
Der Plan benannte vier handgepflegte Stellen als die, die im dkv-Durchlauf
uebersehen wurden, gatete aber nur zwei davon. Ein Ausfuehrender haette Aufgabe 3
abschliessen, jede Pruefung bestehen und Uebersichtszeile, Summenzeile und
Klassen-Verteilung stehen lassen koennen — derselbe Fehler, gegen den der Plan
schuetzen sollte, im Gate des Plans selbst.
Die beiden fehlenden Pruefungen leiten ihre Erwartung ab, statt sie zu raten:
die Uebersichtszeile gegen die am Quelltext neu ermittelten Rohtrefferzahlen
(mit den beiden Befehlen, die das Dokument selbst nennt), die Summenzeile gegen
die Addition der zwoelf Bereichszeilen, die Klassen-Verteilung gegen die ueber
die Bestandsaufnahme nachgezaehlten Klassen samt Summe und Ueberschrift.
Falsifiziert statt behauptet: gruen gegen das heutige, in sich stimmige Dokument;
rot gegen den heutigen unumgestellten Baum; und rot in vier getrennten Mutationen,
je eine stehen gelassene Handstelle — Uebersichtszeile, Summenzeile, Klassenzahl,
Ueberschrift. Gruen erst, wenn Code umgestellt UND alle vier nachgezogen sind.
Zusatzauflage, die daraus folgt: der gebundene Klient heisst in jeder Methode
tenantPrisma (Konvention aus ldap/groups/dkv/auth) — ein anderer Name liesse die
Gebunden-Zaehlung untertreiben und die Zeile ihrer Aussage berauben.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- apps/api/src/dkv/dkv.service.spec.ts (neu): Zwei-Klienten-Nachweis nach
dem Muster aus groups.service.spec.ts/tender-triage.service.spec.ts —
dieser Bereich hatte vorher KEINE Testdatei (Befund J). 7 Testfaelle
decken getConfigForApi, saveConfig (Zugangsdaten-Erhaltung), testConnection,
die Verarbeitungsstrecke und den bewusst ungebundenen Planer-Startpfad ab
- dkv.service.ts: loadConfig(tenantId?) in zwei Methoden geteilt —
loadConfig(tenantId) [Pflicht-Mandant, gebunden] und die neue, eigene
Methode loadAnyActiveConfigForScheduler() [bewusst UNGEBUNDEN, eigener
Kopfkommentar mit beiden Zustaenden]. getConfigForApi/saveConfig/
testConnection/_runPipeline binden je EINEN Klienten pro Methode
vollstaendig ueber forTenant()
- dkv-scheduler.service.ts: Kopfkommentar fortgeschrieben (beide Zustaende,
Praezedenzfall, Unsymmetrie), Aufruf auf loadAnyActiveConfigForScheduler()
umgestellt — an der Ablauflogik des Planers nichts geaendert
- .planning/WINDOWS.md: Eintrag #21 (deviation) fuer die benannte Altlast
des Planer-Startpfads angelegt
- docs/mandantentrennung-zugriffsklassifikation.md: dkvModuleConfig-Zeile
auf den jetzt gemessenen Stand "gemischt" nachgezogen (Rule 3 — noetig,
damit rls-access-inventory.spec.ts nach der Aufteilung von loadConfig()
gruen bleibt; die uebrigen zwei dkv-Zeilen und die Uebersichtstabelle
bleiben Aufgabe 3 vorbehalten)
- Falsifizierungsnachweis erbracht: getConfigForApi's erster gebundener
Client probeweise durch this.prisma ersetzt, genau Test 1 wurde rot
(6 andere blieben gruen), Rueckbau zurueckgenommen, Dateien identisch
zum Ausgangsstand bestaetigt
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- rls-scratch-check.mjs: runDkvAreaChecks() misst die drei ausgelieferten
Policies (DkvInvoiceHistory/DkvModuleConfig/DkvVehicleMaster) wortgleich
aus der Migration, plus die Nebenlaeufigkeitsform von getHistory()
(Promise.all ueber zwei gebundene Einzelabfragen); alle 9 neuen plus
32 bestehende Pruefungen bestehen (41 gesamt)
- Belegt Befund H (kein P2002-Fall, Mandant ist Teil des zusammengesetzten
Schluessels) und Befund I (gebundenes INSERT mit fremder tenantId wird
ohne eigene WITH-CHECK-Klausel trotzdem abgewiesen) an der echten
Datenbank statt am Policy-Text
- docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt
"Bereich dkv" mit der dritten Fehlerform der Etappe (ein Einzelobjekt
wird null, wo null bereits "nicht eingerichtet" bedeutet), der
Signaltabelle je umzustellendem Pfad, den sieben Stellen aus Befund K
(zerstoerend/lautlos/irrefuehrend) und der ausgeschriebenen
Planer-Entscheidung (Form c, mit Unsymmetrie zum ldap-Praezedenzfall)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR