# Mandantentrennung — Zugriffsklassifikation Dieses Dokument gehört zusammen mit `docs/mandantentrennung-datenbankrolle.md` zur Vorbereitung der Mandantentrennung auf Datenbankebene (WINDOWS #18/#20). Während die Datenbankrolle-Anleitung beschreibt, **wie** die Umstellung technisch abläuft, hält dieses Dokument fest, **welche** der 227 `this.prisma.*`-Fundstellen in `apps/api/src` beim Umbau in den folgenden Etappen angefasst werden müssen und welche bewusst unverändert bleiben. Die Bestandsaufnahme unten wird **maschinell aus dem Quelltext ermittelt** (nicht von Hand zusammengeschrieben) und durch `apps/api/src/prisma/rls-access-inventory.spec.ts` bei jedem Testlauf gegen den tatsächlichen Stand geprüft. Verschiebt sich eine Zeile, bleibt die Prüfung grün — Vergleichsschlüssel sind Datei und Prisma-Modellname, keine Zeilennummer. Kommt eine neue Fundstelle hinzu oder verschwindet eine bestehende, schlägt die Prüfung fehl, bis dieses Dokument nachgezogen wird. ## Die drei Klassen - **`muss-mandantengebunden`** — berührt auf Rechnung genau eines Mandanten eine Tabelle mit `tenantId` (oder eine Tabelle, die ihre Mandantenregel über einen Join auf eine solche Tabelle bezieht). Muss in Etappe 2 auf `forTenant()` umgestellt werden. - **`bewusst-uebergreifend`** — muss über Mandanten hinweg sehen, mit ausgeschriebenem Grund. Braucht in Etappe 3 eine sichtbare Kennzeichnung (Systemkontext), aber keinen `forTenant()`-Umbau. - **`keine-mandantengebundene-tabelle`** — betrifft eines der acht Modelle ohne `tenantId` bzw. eine bewusst plattformweite Tabelle (D-03). Kein Umbau nötig. - **`beides`** — ein vierter, im Plan ausdrücklich verlangter Sonderfall: ein Hintergrunddienst, der zurecht über alle Mandanten hinweg eine Liste aufbaut (bewusst übergreifend), aber *innerhalb* der Schleife je Mandant binden muss (muss mandantengebunden werden). Beide Anteile sind in derselben Datei vorhanden; die Fundstelle bekommt hier eine Sammelklassifikation, die Aufteilung auf Zeilenebene steht in der Begründung. ## Zwei belegte Befunde **`req.tenantPrisma` wird gesetzt, aber nirgends gelesen.** `tenant.middleware.ts:44` und `tenant.guard.ts:41` setzen `req.tenantPrisma = forTenant(this.prisma, tenantId)`. Eine Volltextsuche über `apps/api/src` nach `tenantPrisma` außerhalb dieser beiden Dateien und ihrer Tests findet keine lesende Stelle — kein Controller greift darauf zu. Die Verdrahtung besteht, wird aber nicht genutzt. Für Etappe 2 ist zu entscheiden, ob die Controller künftig darüber gehen (dann bräuchte es keinen zweiten `forTenant()`-Aufruf je Service-Methode) oder ob der Weg ersatzlos entfällt. Dieser Plan entscheidet das nicht, hält den Befund nur fest. **WINDOWS #19 — nullbares `tenantId` bei `SearchProvider` und `TenderRssFeedSource`.** Beide Modelle tragen ein nullbares `tenantId` (`SearchProvider` für admin-gepflegte Vorgabe-Suchmaschinen, wobei laut 05-02-Entscheidung die tatsächlichen Vorgaben als Konstanten und nicht als DB-Zeilen mit `tenantId = NULL` geführt werden — die Spalte ist nullbar, ob es heute tatsächlich `NULL`-Zeilen gibt, ist damit eine offene Frage für Etappe 3, nicht eine hier beantwortete; `TenderRssFeedSource` für plattformweite RSS-Quellen wie den geseedeten `service.bund.de`-Feed, D-06). Die aktuelle Policy `"tenantId" = current_tenant_id()` vergleicht `NULL` nie gleich — nach dem Scharfschalten wären plattformweite Zeilen für JEDEN Mandanten unsichtbar, nicht nur für fremde. Das ist heute ohne Wirkung (Schalter aus, #18), muss aber in Etappe 3 zusammen mit den restlichen Fundstellen gelöst werden: die Policy braucht für den Lesezugriff `tenantId IS NULL OR tenantId = current_tenant_id()`, während Schreibzugriffe weiterhin einen Mandanten verlangen. ## Übersicht je Bereich (Zeilentreffer, `this.prisma.*` ohne Specs) Gemessen mit `grep -ro "this\.prisma\.[a-zA-Z]*" apps/api/src/ | grep -v spec | wc -l` am 2026-09-09, **nach** den Änderungen aus Aufgabe 1/2 dieses Plans: | Bereich | Treffer | Hinweis | |---|---|---| | tenders | 62 | unverändert gegenüber measured_baseline | | groups | 37 | unverändert | | ldap | 21 | unverändert | | dkv | 21 | unverändert | | user | 17 | unverändert | | module-registry | 17 | unverändert | | dashboard | 13 | unverändert | | auth | 8 | **war 13 in measured_baseline** — Aufgabe 2 hat 3 Lesezugriffe durch `auth_lookup_*()`-Funktionsaufrufe (`$queryRaw`, kein `this.prisma.`) ersetzt und 5 Schreibzugriffe auf `forTenant()`-gebundene Aufrufe (`tenantPrisma.*`, ebenfalls kein `this.prisma.`) umgestellt | | calendar | 12 | unverändert | | tenant | 8 | unverändert | | favorites | 7 | unverändert | | settings | 4 | unverändert | | **Summe** | **227** | war 232 in measured_baseline, Delta = die 5 in Aufgabe 2 verschwundenen `auth`-Treffer minus ein bereits vorher fehlerhaft mitgezähltes Kommentarvorkommen in der neuen Kopfzeile von `validateUser()`, das bewusst umformuliert wurde, um einen Eigentreffer der Bestandsaufnahme-Prüfung zu vermeiden | ## Klassen-Verteilung (nach (Datei, Modell)-Fundstellen, 61 Paare) Stand 260909-ipc (Aufgabe 2): 59 Paare aus der urspruenglichen Zaehlung plus zwei bisher unentdeckte, weil bereits gebundene Fundstellen (`auth.service.ts`/`passwordResetToken`, `ldap.service.ts`/`groupMembership`), die erst die um gebundene Zugriffe erweiterte Erkennung (Befund G) sichtbar macht — sie waren nie Teil der 227 `this.prisma.*`-Rohtrefferzahl, weil sie schon vor diesem Plan über `forTenant()` liefen. Dazu die Korrektur von (`ldap-config.service.ts`, `ldapConfig`) von `muss-mandantengebunden` auf `beides` (Befund B). | Klasse | Anzahl Paare | |---|---| | muss-mandantengebunden | 32 | | keine-mandantengebundene-tabelle | 16 | | beides | 10 | | bewusst-uebergreifend | 3 | | **Summe** | **61** | ## Der Hintergrunddienst als Falle — drei `beides`-Fälle Ein Planer, der über alle Mandanten iteriert, liest zu Recht übergreifend — muss aber *innerhalb* der Schleife je Mandant binden. Drei Dateien sind betroffen: - **`ldap.service.ts`** (AD-Abgleich): iteriert nicht selbst über alle Mandanten (der Sync läuft je Aufruf für einen übergebenen Mandanten), aber innerhalb der Sync-Methoden bleiben Lesezugriffe auf `group`, `ldapConfig` und `user` teils ungebunden, obwohl der Mandant zu diesem Zeitpunkt bereits bekannt ist — die 4 echten `forTenant()`-Aufrufstellen (Zeilen 762, 905, 1179, 1342) decken nur einen Teil der Lese-/Schreibpfade ab. Das ist der im Plankontext benannte Kern von WINDOWS #20: genau dieser Löschzweig (~Zeile 1559) deutet Leere nach dem Scharfschalten als "Gruppe im Verzeichnis verschwunden". - **`tender-digest.scheduler.ts`** (Ausschreibungs-Digest): liest `tenderMatch`/`tenderNotificationPref`/`user` bewusst über ALLE Mandanten in einem `findMany` (ein einziger globaler Cron-Job, kein Mandant im Job selbst — so von Anfang an entworfen, Pitfall 1 in den Kommentaren der Datei). Innerhalb der Verteilung je Treffer ist der Mandant aus der Zeile bekannt und der Versand muss darauf gebunden laufen. - **`tender-matching.service.ts`** (Ausschreibungs-Sofortmeldung): dieselbe Form — `tenderMatch`/`tenderSavedSearch`/`user` werden für die Sofort-Benachrichtigung über alle betroffenen Mandanten hinweg gelesen, der Versand je Treffer ist mandantengebunden. ## Bestandsaufnahme Maschinell ermittelt, `rls-access-inventory.spec.ts` hält Vollständigkeit nach. Spalten: Datei, Modell (Prisma-Modellname wie in `this.prisma.` oder — seit 260909-ipc, Befund G — in `.` verwendet), Klasse, Stand (`gebunden`/`ungebunden`/`gemischt`, seit 260909-ipc maschinell gegen den Quelltext geprüft), Begründung. | Datei | Modell | Klasse | Stand | Begründung | |---|---|---|---|---| | apps/api/src/auth/auth.service.ts | passwordResetToken | muss-mandantengebunden | gebunden | Kein eigenes `tenantId`, RLS ueber Join auf `User` (Migration 20260618112133). `requestPasswordReset`/`resetPassword` laufen vollstaendig ueber `forTenant()` (Etappe 1, WINDOWS #20, Aufgabe 1) — von der alten, nur `this.prisma.*` erkennenden Suche nie erfasst, weil bereits gebunden; die erweiterte Erkennung aus Aufgabe 2 (260909-ipc) macht diese Fundstelle erstmals sichtbar. | | apps/api/src/auth/auth.service.ts | user | muss-mandantengebunden | gemischt | `getMe`, `changePassword`, `adminResetPassword` suchen über die Benutzerkennung aus dem Sitzungsnachweis — der Mandant ist dort bereits bekannt (Aufgabe 2 fasst sie bewusst nicht an, siehe SUMMARY). | | apps/api/src/calendar/calendar.service.ts | calendarSource | muss-mandantengebunden | ungebunden | Kalenderquellen eines Nutzers, `tenantId`-Spalte vorhanden. | | apps/api/src/dashboard/dashboard.service.ts | dashboardLayout | muss-mandantengebunden | ungebunden | Widget-Anordnung eines Nutzers, `tenantId`-Spalte vorhanden. | | apps/api/src/dashboard/dashboard.service.ts | module | keine-mandantengebundene-tabelle | ungebunden | Modulkatalog ist plattformweit, kein `tenantId` (Migration 20260909140000, Gruppe b). | | apps/api/src/dashboard/dashboard.service.ts | searchProvider | muss-mandantengebunden | ungebunden | `tenantId` nullbar (WINDOWS #19) — heutige, tatsächlich gespeicherte Zeilen sind nutzerangelegt und tragen einen Mandanten; Vorgabe-Anbieter kommen laut 05-02 aus Konstanten, nicht aus der DB. | | apps/api/src/dashboard/dashboard.service.ts | widgetInstance | muss-mandantengebunden | ungebunden | Platzierte Dashboard-Widgets eines Nutzers, `tenantId`-Spalte vorhanden. | | apps/api/src/dkv/dkv.service.ts | dkvInvoiceHistory | muss-mandantengebunden | ungebunden | DKV-Rechnungshistorie je Mandant, `tenantId`-Spalte vorhanden. | | apps/api/src/dkv/dkv.service.ts | dkvModuleConfig | muss-mandantengebunden | ungebunden | Postfach-/Zugangsdaten des DKV-Moduls je Mandant. | | apps/api/src/dkv/dkv.service.ts | dkvVehicleMaster | muss-mandantengebunden | ungebunden | Fahrzeugstammdaten des DKV-Moduls je Mandant. | | apps/api/src/favorites/favorites.service.ts | favoriteLink | muss-mandantengebunden | ungebunden | Favoriten-Links eines Nutzers, `tenantId`-Spalte vorhanden. | | apps/api/src/groups/groups.service.ts | group | muss-mandantengebunden | ungebunden | Gruppen sind je Mandant, `tenantId`-Spalte vorhanden. | | apps/api/src/groups/groups.service.ts | groupMembership | muss-mandantengebunden | ungebunden | Kein eigenes `tenantId`, RLS über Join auf `Group` (Migration 20260618112133-Nachfolger) — braucht trotzdem `forTenant()`, damit der Join-Kontext gesetzt ist. | | apps/api/src/groups/groups.service.ts | moduleGrant | muss-mandantengebunden | ungebunden | Modulfreigaben je Mandant, `tenantId`-Spalte vorhanden. | | apps/api/src/groups/groups.service.ts | user | muss-mandantengebunden | ungebunden | Nutzerverwaltung innerhalb eines Mandanten. | | apps/api/src/groups/module-grants.service.ts | group | muss-mandantengebunden | ungebunden | Wie groups.service.ts. | | apps/api/src/groups/module-grants.service.ts | groupMembership | muss-mandantengebunden | ungebunden | Kein eigenes `tenantId`, RLS über Join auf `Group`. | | apps/api/src/groups/module-grants.service.ts | moduleGrant | muss-mandantengebunden | ungebunden | Modulfreigaben je Mandant. | | apps/api/src/groups/module-grants.service.ts | tenantModuleActivation | muss-mandantengebunden | ungebunden | Welche Module ein Mandant aktiviert hat, `tenantId`-Spalte vorhanden. | | apps/api/src/groups/module-grants.service.ts | user | muss-mandantengebunden | ungebunden | Zielbenutzer eines Grants innerhalb des Mandanten. | | apps/api/src/ldap/ldap-config.service.ts | ldapConfig | beides | gemischt | `getConfig`, `createConfig` und `updateConfig` laufen ueber `forTenant()`, gebunden an den aus der Anfrage bereits bekannten Mandanten. `getAllActiveConfigs()` (Planer-Lesezugriff ueber ALLE Mandanten) und die Start-Nachverschluesselung in `onApplicationBootstrap` bleiben bewusst uebergreifend: beide laufen, bevor bzw. unabhaengig davon, ob ein einzelner Mandantenkontext feststeht (Befund B, 260909-ipc-PLAN.md). Korrektur der Klasse von `muss-mandantengebunden`: das Paar ist tatsaechlich `beides`, keine Verhaltensaenderung. | | apps/api/src/ldap/ldap-config.service.ts | ldapFieldMapping | muss-mandantengebunden | gebunden | Kein eigenes `tenantId`, RLS ueber Join auf `LdapConfig`. `addFieldMapping` und `removeFieldMapping` nehmen den Mandanten jetzt als Parameter entgegen und laufen vollstaendig ueber `forTenant()` (T-IPC-01, 260909-ipc-PLAN.md) — schliesst zugleich die Fremdzugriffsluecke beim Loeschen einer Feldzuordnung ueber ihre Kennung. | | apps/api/src/ldap/ldap.service.ts | group | beides | gemischt | AD-Abgleich: 4 echte `forTenant()`-Aufrufstellen decken einen Teil ab, weitere `this.prisma.group`-Zugriffe innerhalb der Sync-Methoden bleiben ungebunden, obwohl der Mandant zu diesem Zeitpunkt bekannt ist (siehe Abschnitt "Der Hintergrunddienst als Falle"). | | apps/api/src/ldap/ldap.service.ts | groupMembership | muss-mandantengebunden | gebunden | Kein eigenes `tenantId`, RLS ueber Join auf `Group`. `syncGroupMembershipsForTenant` laeuft vollstaendig ueber `forTenant()` (Plan 16-03/16-05) — von der alten, nur `this.prisma.*` erkennenden Suche nie erfasst, weil bereits gebunden; die erweiterte Erkennung aus Aufgabe 2 (260909-ipc) macht diese Fundstelle erstmals sichtbar. | | apps/api/src/ldap/ldap.service.ts | ldapConfig | beides | ungebunden | Dieselbe Begründung wie `group` — Konfigurationszugriffe innerhalb der Sync-Methoden. | | apps/api/src/ldap/ldap.service.ts | user | beides | gemischt | Dieselbe Begründung — der Löschzweig um Zeile 1559 (WINDOWS #20) ist der konkrete Risikofall. | | apps/api/src/module-registry/module-access.service.ts | module | keine-mandantengebundene-tabelle | ungebunden | Modulkatalog ist plattformweit, kein `tenantId`. | | apps/api/src/module-registry/module-access.service.ts | moduleGrant | muss-mandantengebunden | ungebunden | Modulfreigaben je Mandant. | | apps/api/src/module-registry/module-access.service.ts | tenantModuleActivation | muss-mandantengebunden | ungebunden | Aktivierung je Mandant, `tenantId`-Spalte vorhanden. | | apps/api/src/module-registry/module-registry.service.ts | module | keine-mandantengebundene-tabelle | ungebunden | Modulkatalog ist plattformweit. | | apps/api/src/module-registry/module-registry.service.ts | tenantModuleActivation | muss-mandantengebunden | ungebunden | Aktivierung je Mandant. | | apps/api/src/settings/settings.service.ts | smtpConfig | muss-mandantengebunden | ungebunden | SMTP-Zugangsdaten je Mandant, `tenantId`-Spalte vorhanden. | | apps/api/src/tenant/tenant.controller.ts | tenant | keine-mandantengebundene-tabelle | ungebunden | `Tenant` ist die Mandantentabelle selbst — hat keine eigene `tenantId`-Spalte, kann sie per Definition nicht haben (Migration 20260909140000, Gruppe b). | | apps/api/src/tenant/tenant.service.ts | tenant | keine-mandantengebundene-tabelle | ungebunden | Dieselbe Begründung. | | apps/api/src/tenders/adapters/email-alert.adapter.ts | tenderEmailConfig | bewusst-uebergreifend | ungebunden | `fetchTenders()` liest bewusst jede aktive `TenderEmailConfig`-Zeile über ALLE Mandanten in einer Abfrage (Plattform-Scheduler, ein Tick pro Postfach, D-13/D-01) — ausführlich im Dateikopf begründet, darf laut Kommentar niemals in `forTenant()` verpackt werden. | | apps/api/src/tenders/adapters/rss.adapter.ts | tenderRssFeedSource | bewusst-uebergreifend | ungebunden | Fan-out über jeden aktiven Feed, plattformweit UND persönlich, in einer Abfrage (Zeilen 55–83 im Dateikopf begründet) — dieselbe Scheduler-Ebene wie beim E-Mail-Adapter. | | apps/api/src/tenders/tender-dedup.service.ts | tender | keine-mandantengebundene-tabelle | ungebunden | Explizit im Dateikopf: "platform-global, RLS-exempt tables. Never wrap these queries in forTenant()." (D-03) | | apps/api/src/tenders/tender-dedup.service.ts | tenderSource | keine-mandantengebundene-tabelle | ungebunden | Dieselbe Begründung. | | apps/api/src/tenders/tender-digest.scheduler.ts | tenderMatch | beides | ungebunden | Ein einziger globaler Cron-Job liest über ALLE Mandanten (bewusst übergreifend, Pitfall-1-Kommentar im Dateikopf), der Versand je Treffer ist an dessen Mandanten gebunden. | | apps/api/src/tenders/tender-digest.scheduler.ts | tenderNotificationPref | beides | ungebunden | Dieselbe Begründung — Präferenzen werden über alle Mandanten gelesen, aber je Zeile mandantenbezogen ausgewertet. | | apps/api/src/tenders/tender-digest.scheduler.ts | user | beides | ungebunden | E-Mail-Adressen für den Versand werden über alle Mandanten gelesen, der eigentliche Versand ist je Treffer mandantengebunden. | | apps/api/src/tenders/tender-email-config.service.ts | tenderEmailConfig | muss-mandantengebunden | ungebunden | Nutzer-CRUD für die eigene Postfachanbindung (Phase 17, D-01) — anders als der Fan-out-Adapter oben, hier ist der Mandant aus der Anfrage bekannt. | | apps/api/src/tenders/tender-fingerprint-backfill.service.ts | tender | keine-mandantengebundene-tabelle | ungebunden | Einmaliges Backfill-Skript über den plattformweiten `Tender`-Katalog (D-03). | | apps/api/src/tenders/tender-ingestion.service.ts | tender | keine-mandantengebundene-tabelle | ungebunden | Explizit im Dateikopf: "Multi-tenant safety (D-03, T-10-09): uses the plain, non-tenant-scoped ... queries ... these are platform-global". | | apps/api/src/tenders/tender-ingestion.service.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | ungebunden | Plattformweiter Poll-Status, kein `tenantId` (Migration 20260909140000, Gruppe b). | | apps/api/src/tenders/tender-matching.service.ts | tender | keine-mandantengebundene-tabelle | ungebunden | Liest den plattformweiten Katalog (D-03), um Treffer zu berechnen — kein `tenantId`. | | apps/api/src/tenders/tender-matching.service.ts | tenderMatch | beides | ungebunden | Sofortmeldung: Treffer über alle betroffenen Mandanten gelesen, Versand je Treffer mandantengebunden — siehe Abschnitt "Der Hintergrunddienst als Falle". | | apps/api/src/tenders/tender-matching.service.ts | tenderSavedSearch | beides | ungebunden | Dieselbe Begründung — gespeicherte Suchprofile aller Mandanten werden gegen neue Treffer geprüft, der Versand ist je Profil mandantengebunden. | | apps/api/src/tenders/tender-matching.service.ts | user | beides | ungebunden | Dieselbe Begründung — E-Mail-Adressen für die Sofortmeldung. | | apps/api/src/tenders/tender-notification-pref.service.ts | tenderNotificationPref | muss-mandantengebunden | ungebunden | Nutzer-CRUD für die eigenen Benachrichtigungseinstellungen — Mandant aus der Anfrage bekannt. | | apps/api/src/tenders/tender-rss-feed.service.ts | tenderRssFeedSource | muss-mandantengebunden | ungebunden | Nutzer-CRUD für die eigenen RSS-Quellen (anders als der Fan-out in `adapters/rss.adapter.ts`) — Mandant aus der Anfrage bekannt. WINDOWS #19 betrifft die nullbaren plattformweiten Zeilen, nicht diesen CRUD-Pfad. | | apps/api/src/tenders/tender-saved-search.service.ts | tenderSavedSearch | muss-mandantengebunden | ungebunden | Nutzer-CRUD für die eigenen gespeicherten Suchprofile — Mandant aus der Anfrage bekannt. | | apps/api/src/tenders/tender-scheduler.service.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | ungebunden | Plattformweiter DÖE-Poll-Status, kein `tenantId` — im Dateikopf explizit als "genuine platform-wide singleton" begründet. | | apps/api/src/tenders/tender-triage.service.ts | tenderTriage | muss-mandantengebunden | ungebunden | Favorisierungs-/Ablehnungsstatus eines Nutzers, `tenantId`-Spalte vorhanden. | | apps/api/src/tenders/tenders.controller.ts | tender | keine-mandantengebundene-tabelle | ungebunden | Lesezugriff auf den plattformweiten Katalog (D-03). | | apps/api/src/tenders/tenders.controller.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | ungebunden | Plattformweiter Poll-Status, admin-verwaltet, kein `tenantId`. | | apps/api/src/tenders/tenders.module.ts | tenderSourcePollConfig | keine-mandantengebundene-tabelle | ungebunden | Singleton-Bestückung beim Boot — im Dateikopf explizit als "global, RLS-exempt (D-03)" begründet. | | apps/api/src/user/admin-seed.service.ts | tenant | keine-mandantengebundene-tabelle | ungebunden | Legt beim ersten Start den Standard-Mandanten selbst an — `Tenant` hat keine `tenantId`-Spalte. | | apps/api/src/user/admin-seed.service.ts | user | bewusst-uebergreifend | ungebunden | Erstanlage des Administrators beim ersten Start: läuft einmalig beim Boot, BEVOR irgendein Mandantenkontext existiert, um den allerersten Mandanten samt Admin-Nutzer anzulegen — es gibt zu diesem Zeitpunkt strukturell keinen Mandanten, an den gebunden werden könnte. | | apps/api/src/user/user.controller.ts | user | muss-mandantengebunden | ungebunden | Nutzerverwaltung innerhalb des Mandanten des anfragenden Admins. | | apps/api/src/user/user.service.ts | user | muss-mandantengebunden | ungebunden | Dieselbe Begründung. | ## Was diese Etappe NICHT entscheidet - Ob Controller künftig über `req.tenantPrisma` statt eines erneuten `forTenant()`-Aufrufs im Service gehen (offener Befund oben). - Wie die WINDOWS-#19-Policy für `SearchProvider`/`TenderRssFeedSource` am Ende genau lautet — nur, dass sie vor dem Scharfschalten gelöst sein muss. - Die Reihenfolge und Zuschnitt der Etappe-2-Pläne — dafür ist die Klassen-Verteilung oben der Arbeitsvorrat, siehe `` im Plan `260909-eor-PLAN.md`.