feat(quick-260910-exd): ModuleRegistryService binden, Klassifikation abschliessen

- 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
This commit is contained in:
2026-09-10 11:30:37 +02:00
parent 3df72687c1
commit 9c0eefee90
6 changed files with 515 additions and 17 deletions
@@ -1457,6 +1457,89 @@ und verlöre ihr Signal.
- Schema und Migrationen — geprüft und bewusst gelassen, keine
Schemaänderung in dieser Etappe.
**Nachtrag (260910-exd, Aufgabe 3).** Wie in den vorherigen Durchläufen wird
der Text oben NICHT umgeschrieben — er beschreibt korrekt den Stand zum
Zeitpunkt der Messung (Aufgabe 1); dieser Nachtrag hält fest, was Aufgabe 2/3
tatsächlich umgesetzt haben.
*Tatsächlich umgesetzte Pfade gegen die in (m2) angekündigten gehalten:*
alle in (m2) genannten Pfade sind wie beschrieben umgestellt.
`ModuleAccessService.getAccessibleModuleIds` bindet Kurzschlusszweig,
Direktweg, Gruppenweg und Schnittmenge über EINEN Klienten je Aufruf;
`getCatalogFlags` bindet ihren eigenen Aktivierungs-Lesezugriff, die
geschachtelte `getAccessibleModuleIds`-Auflösung erzeugt ihren eigenen
Klienten; `findAccessibleModules` erreicht den Katalog weiterhin über den
ungebundenen Klienten. `ModuleRegistryService.findActiveForTenant`,
`activateForTenant` und `isModuleActive` binden ihren jeweiligen
Aktivierungszugriff; `deactivateForTenant` bindet beide Aktivierungszugriffe
(Lesen, Schreiben) über EINEN Klienten. Alle sechs Katalogzugriffe in
`module-registry.service.ts` (`findAll`, `findBySlug`, die beiden
Katalog-Existenzprüfungen, die Katalogsuche in `isModuleActive`,
`seedModule`) und der eine Katalogzugriff in `module-access.service.ts`
(`findAccessibleModules`) blieben wie angekündigt ungebunden, mit
Kommentaren, die Messung und Bedingung trennen. Beide falschen
Kopfkommentare aus Befund G sind behandelt: `isModuleActive`s Kommentar ist
richtiggestellt (mit Bezug auf die Aufrufermessung aus TEIL 3); der
irreführende Satz im Kopfkommentar von `module-registry.controller.ts`
("findActiveForTenant on ModuleRegistryService stays unchanged for Plan
15-03's marketplace catalog") wurde NICHT mitgeändert — der Controller ist
nicht Teil dieses Plans. Die Feststellung, dass diese Aussage falsch ist
(der Marktplatz-Katalog wird nachweislich von `getCatalogFlags` bedient,
nicht von `findActiveForTenant`), steht stattdessen hier in (m5).
*Deviation (Rule 1/3): eine cross-area Testabhängigkeit brach durch die
Umstellung.* `tender-scheduler.service.spec.ts` instanziiert
`ModuleRegistryService` unmocked gegen einen hand-gerollten Fake ohne
`$extends` (derselbe Zweck wie in `ldap.service.spec.ts`: der Aktivierungs-
Aufruf soll echt sein, nicht ein Stand-in). Nach der Umstellung von
`activateForTenant` auf `forTenant()` scheiterte dieser Test mit
`prisma.$extends is not a function`. Behoben mit derselben Konvention wie
`ldap.service.spec.ts` — `forTenant` in dieser einen Datei über `vi.mock`
auf eine Identitätsfunktion gelegt (`forTenant: vi.fn((p) => p)`), weil die
Datei RLS-Bindungsmechanik nicht testet, nur das Poll-once-fan-out-many-
Verhalten des Schedulers. Kein anderer Aufrufer von `new
ModuleRegistryService(...)` existiert im Quelltext (geprüft).
*Deviation (Rule 3): die Klassifikationsdokument-Stände für
`module-access.service.ts` wurden bereits in Aufgabe 2 nachgezogen*, nicht
erst in dieser Aufgabe — `rls-access-inventory.spec.ts` ist Teil der von
Aufgabe 2 verlangten vollständigen Testsuite und wäre sonst am Ende von
Aufgabe 2 bereits rot gewesen. Diese Abweichung von der Aufgabenaufteilung
(die Klassifikationsdatei war für Aufgabe 3 vorgesehen) ist auf das
Notwendige beschränkt: nur die `Stand`-Spalte der beiden betroffenen Zeilen,
keine Begründung, keine Zahlen. Die vollständige Nachziehung (fünf
Bestandsaufnahme-Zeilen inklusive `module-registry.service.ts`,
Übersichtszeile, Summenzeile, Klassen-Verteilung, Hintergrunddienst-
Abschnitt) erfolgte wie geplant in dieser Aufgabe.
*Falsifizierungsnachweise (Aufgabe 2 und 3), je einmal durchgeführt und
zurückgenommen:* in Aufgabe 2 wurde die Gruppenweg-Bindung der
Freigabe-Auflösung probeweise zurückgebaut (`tenantPrisma.moduleGrant` →
`this.prisma.moduleGrant` im Gruppenweg von `getAccessibleModuleIds`) —
genau `module-access.service.spec.ts`, Test "ModuleAccessService — Bindung
an forTenant() (260910-exd) > USER-Zweig bindet BEIDE
Freigabe-Lesezugriffe (Direktweg und Gruppenweg) UND den
Schnittmengen-Lesezugriff an DIESELBE Mandantenkennung", wurde rot, mit der
Meldung `expected 1 to be 2`; der Rückbau wurde zurückgenommen, derselbe
Testlauf danach wieder grün (23/23). In Aufgabe 3 wurde der
Schreibzugriff des Deaktivierens probeweise zurückgebaut
(`tenantPrisma.tenantModuleActivation.update` →
`this.prisma.tenantModuleActivation.update` in `deactivateForTenant`) —
genau `module-registry.service.spec.ts`, Test
"ModuleRegistryService.deactivateForTenant > bindet beide
Aktivierungszugriffe (Lesen, Schreiben) an denselben Mandanten, über einen
Klienten", wurde rot, mit der Meldung "erwarteter gebundener Aufruf
tenantModuleActivation.update(tenant=t1) fehlt im Protokoll:
[{"tenantId":"t1","model":"tenantModuleActivation","method":"findUnique"}]:
expected false to be true"; der Rückbau wurde zurückgenommen, derselbe
Testlauf danach wieder grün (16/16).
*Die offene WINDOWS-Aufzeichnung.* Eintrag #23 (`deviation`) hält fest, dass
es kein Signal gibt, das "wirklich keine Freigabe" von "die Abfrage hat
nichts gefunden" unterscheidet, mit der Vorabprüfung für Etappe 4 und der
begründeten Verwerfung einer Laufzeitwarnung — siehe (m3) oben und
`.planning/WINDOWS.md`.
## Verweis
Die Bestandsaufnahme, welche Fundstelle den hier beschriebenen Übergang