docs(quick-260910-jab): Aktenstand kohaerent machen — Ledger, Klassifikation, Kritikschrift, Betriebsanleitung

- 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
This commit is contained in:
2026-09-10 14:44:34 +02:00
parent 6b237351e9
commit 03fb3bf9c7
4 changed files with 263 additions and 34 deletions
@@ -48,20 +48,40 @@ 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.
`TenderRssFeedSource` — GESCHLOSSEN (260910-jab, Aufgabe 1/2).** Beide Modelle
tragen ein nullbares `tenantId` (`SearchProvider` für admin-gepflegte
Vorgabe-Suchmaschinen; `TenderRssFeedSource` für plattformweite RSS-Quellen
wie den geseedeten `service.bund.de`-Feed, D-06). Die ausgelieferte Policy
`"tenantId" = current_tenant_id()` verglich `NULL` nie gleich — nach dem
Scharfschalten wären plattformweite Zeilen für JEDEN Mandanten unsichtbar
gewesen, nicht nur für fremde.
Geschlossen durch Migration
`20260910120000_rls_widen_membership_grant_and_platform_read`, lokal
angewandt und gegen den Systemkatalog der lebenden Datenbank gemessen:
`TenderRssFeedSource` bekommt vier nach Befehl getrennte Regeln —
`tenant_platform_read_policy` (SELECT) schließt Zeilen ohne Mandant
ausdrücklich ein, `tenant_insert_policy`/`tenant_update_policy`/
`tenant_delete_policy` verlangen weiterhin ausnahmslos einen Mandanten. Die
Trennung nach Befehl ist notwendig, weil ein einzelner `USING`-Ausdruck auch
bestimmt, welche Zeilen `UPDATE`/`DELETE` erreichen — eine Leseregel, die
plattformweite Zeilen einschließt, hätte ohne Trennung jedem Mandanten auch
das Ändern/Entfernen dieser Zeilen erlaubt.
Die Hälfte zur Suchanbietertabelle (`SearchProvider`) schließt NICHT als
gelöstes Problem, sondern als **widerlegte Prämisse**: lokal gemessen gibt es
keinen Codeweg, der eine mandantenlose `SearchProvider`-Zeile erzeugt — der
einzige Schreibweg (`dashboard.service.ts`) verlangt die Mandantenkennung als
Pflichtparameter, und die Vorgabe-Suchmaschinen kommen laut 05-02-Entscheidung
aus Konstanten, nicht aus der Datenbank. Die Regel bleibt deshalb bewusst
unverändert streng; eine Lockerung wäre hier die falsche Richtung, weil sie
eine künftige mandantenlose Zeile jedem Mandanten zeigen würde.
Was diese Reparatur NICHT löst: unter der Anwendungsrolle lässt sich eine
plattformweite `TenderRssFeedSource`-Zeile weder anlegen noch entfernen, in
der alten wie in der neuen Regel, weil jede Schreibregel einen Mandanten
verlangt. Als eigener offener Ledger-Eintrag festgehalten (WINDOWS #24),
damit dieser Rest nicht mit #19 verschwindet.
## Übersicht je Bereich (Zeilentreffer je Bereich, ungebunden vs. gebunden)
@@ -96,7 +116,7 @@ autoritative Quelle.
| Bereich | Ungebunden | Gebunden | Hinweis |
|---|---|---|---|
| tenders | 36 | 26 | **war 62/0** — Aufgabe 2/3 (260909-laa) haben die fünf Nutzer-CRUD-Dienste (vier vollständig, `tender-rss-feed.service.ts` teilweise mit im Code begründeter WINDOWS-#19-Grenze) und die Je-Treffer-Hälften der beiden Hintergrunddienste (`tender-digest.scheduler.ts`, `tender-matching.service.ts`) auf `forTenant()` umgestellt. Die 36 verbleibenden ungebundenen Treffer sind die zwölf bewusst nicht angefassten Paare (D-03-Katalog, zwei Fan-out-Adapter) plus die drei bewusst ungebundenen RSS-Pfade plus die übergreifenden Hälften der beiden Hintergrunddienste (Etappe-3-Übergabe) |
| tenders | 35 | 27 | **war 62/0**, dann 36/26 nach 260909-laa — 260910-jab (Aufgabe 2) hat `tender-rss-feed.service.ts`/`listForUser` zusätzlich auf `forTenant()` umgestellt (WINDOWS #19 geschlossen, Befund F: ungebunden hätte die Reparatur den Pfad sonst still auf nur die plattformweiten Zeilen reduziert): ein Rohtreffer wandert von ungebunden nach gebunden (36→35, 26→27). Die 35 verbleibenden ungebundenen Treffer sind die zwölf bewusst nicht angefassten Paare (D-03-Katalog, zwei Fan-out-Adapter) plus die zwei bewusst ungebundenen RSS-Pfade (`createPlatform`/`remove`, WINDOWS #24) plus die übergreifenden Hälften der beiden Hintergrunddienste (Etappe-3-Übergabe) |
| groups | 0 | 31 | **war 37/0** — Aufgabe 2/3 (260909-jts) haben `groups.service.ts` (12 Methoden) und `module-grants.service.ts` (5 Methoden) vollständig auf `forTenant()`/`withTenantTransaction()` umgestellt. Die neun zusätzlichen, über `tx` gebundenen Zugriffe innerhalb der drei Transaktionen zählt dieses einfache Muster nicht mit (siehe Methodenhinweis oben) |
| ldap | 4 | 26 | **war 21/0** — Aufgabe 2/3 (260909-ipc) haben `ldap-config.service.ts` (5 Methoden) und `ldap.service.ts` (6 Methoden, 11 Abfragen) auf `forTenant()` umgestellt. Die 4 verbleibenden ungebundenen Treffer sind bewusst: `getAllActiveConfigs`/`onApplicationBootstrap` (Befund B) und `resolveEmailForWrite` (Befund A, T-IPC-04) |
| dkv | 1 | 22 | **war 21/0** — Aufgabe 2/3 (260909-mir) haben `dkv.service.ts` vollständig auf `forTenant()` umgestellt: Konfigurationspfade (`loadConfig`, `getConfigForApi`, `saveConfig`, `testConnection`), Historie, Fahrzeugstammdaten und der neue Besitzriegel vor dem Ausfuhrdatei-Download. Gebunden sind es 22 statt 21, weil der Riegel einen zusätzlichen Lesezugriff auf `dkvInvoiceHistory` einführt (T-MIR-03). Der eine verbleibende ungebundene Treffer ist der benannte Planer-Startpfad `loadAnyActiveConfigForScheduler()` (Befund D, WINDOWS #21) — bewusst, mit dreifacher Markierung |
@@ -108,7 +128,7 @@ autoritative Quelle.
| tenant | 8 | 0 | unverändert |
| favorites | 7 | 0 | unverändert |
| settings | 4 | 0 | unverändert |
| **Summe** | **108** | **134** | Ungebunden: war 118 nach 260910-das, Delta = die 10 in Aufgabe 2/3 (260910-exd) gesunkenen `module-registry`-Rohtreffer (17→7). Gebunden: war 124, jetzt zusätzlich 10 in `module-registry`. Diese Übersicht ist eine Buchführungshilfe; **autoritativ ist die Fundstellentabelle unten**, die `rls-access-inventory.spec.ts` bei jedem Lauf gegen den Quelltext prüft |
| **Summe** | **107** | **135** | Ungebunden: war 118 nach 260910-das, dann 108 nach 260910-exd (module-registry 17→7), jetzt 107 nach 260910-jab (`tenders` 36→35, `listForUser` gebunden). Gebunden: war 124, dann 134 nach 260910-exd (zusätzlich 10 in `module-registry`), jetzt 135 nach 260910-jab (zusätzlich 1 in `tenders`). Diese Übersicht ist eine Buchführungshilfe; **autoritativ ist die Fundstellentabelle unten**, die `rls-access-inventory.spec.ts` bei jedem Lauf gegen den Quelltext prüft |
## Klassen-Verteilung (nach (Datei, Modell)-Fundstellen, 63 Paare)
@@ -276,7 +296,7 @@ verwendet), Klasse, Stand (`gebunden`/`ungebunden`/`gemischt`, seit
| 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 | searchProvider | muss-mandantengebunden | ungebunden | `tenantId` nullbar. WINDOWS #19 geschlossen (260910-jab) als **widerlegte Prämisse** für dieses Modell: lokal gemessen null Zeilen mit `tenantId = NULL` insgesamt, dieser Schreibweg verlangt die Mandantenkennung als Pflichtparameter; Vorgabe-Anbieter kommen laut 05-02 aus Konstanten, nicht aus der DB. Die Regel auf `SearchProvider` bleibt deshalb unverändert streng. |
| 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 | gebunden | DKV-Rechnungshistorie je Mandant, `tenantId`-Spalte vorhanden. Seit 260909-mir (Aufgabe 3) laufen beide Historien-Schreibzugriffe der Verarbeitungsstrecke, beide parallelen Lesezugriffe von `getHistory` und der neue Riegel vor dem Ausfuhrdatei-Download vollstaendig ueber `forTenant()`. |
| apps/api/src/dkv/dkv.service.ts | dkvModuleConfig | muss-mandantengebunden | gemischt | Postfach-/Zugangsdaten des DKV-Moduls je Mandant. Seit 260909-mir (Aufgabe 2) laufen `loadConfig`, `getConfigForApi`, `saveConfig`, `testConnection` und der Konfigurations-Lesezugriff der Verarbeitungsstrecke ueber `forTenant()`. Die Mischung stammt ausschliesslich vom einen benannten, bewusst ungebundenen Planer-Startpfad `loadAnyActiveConfigForScheduler()` (WINDOWS #21) — keine uebersehene Fundstelle. |
@@ -286,10 +306,10 @@ verwendet), Klasse, Stand (`gebunden`/`ungebunden`/`gemischt`, seit
| apps/api/src/groups/groups.service.ts | groupMembership | muss-mandantengebunden | gebunden | Kein eigenes `tenantId`, RLS über Join auf `Group` (Migration 20260618112133-Nachfolger) — braucht trotzdem `forTenant()`, damit der Join-Kontext gesetzt ist. Seit 260909-jts gebunden, einschliesslich der drei Zugriffe innerhalb des Standardgruppen-Aufbaus (`ensureDefaultGroup`), die zuvor ueber den Transaktionsparameter liefen und fuer keine Pruefung dieses Projekts sichtbar waren (Befund B). |
| apps/api/src/groups/groups.service.ts | moduleGrant | muss-mandantengebunden | gebunden | Modulfreigaben je Mandant, `tenantId`-Spalte vorhanden. Seit 260909-jts gebunden, einschliesslich des Zugriffs innerhalb des Standardgruppen-Aufbaus (Befund B). |
| apps/api/src/groups/groups.service.ts | tenantModuleActivation | muss-mandantengebunden | gebunden | Welche Module ein Mandant aktiviert hat. Bis 260909-jts vollstaendig unsichtbar (Befund B, 260909-jts-PLAN.md): der einzige Zugriff dieser Datei lief innerhalb der interaktiven Transaktion von `ensureDefaultGroup` ueber den Rueckgabeparameter (`tx.tenantModuleActivation.findMany`) — weder `this.prisma.<Modell>` noch `<gebundener Client>.<Modell>` sahen das, weil `tx` weder `this.prisma` noch aus einer `forTenant(`-Zuweisung stammte. Die um Transaktionsparameter erweiterte Erkennung aus Aufgabe 2 macht dieses Paar erstmals sichtbar; die Stelle ist seit derselben Aufgabe gebunden (ueber `withTenantTransaction()`). |
| apps/api/src/groups/groups.service.ts | user | muss-mandantengebunden | gebunden | Nutzerverwaltung innerhalb eines Mandanten. Seit 260909-jts gebunden; `addUserToDefaultGroup` prueft seither zusaetzlich, dass der Zielbenutzer zum Mandanten gehoert (Befund E, T-JTS-02) — die Regel auf `GroupMembership` prueft nachweislich nur die Gruppenseite. |
| apps/api/src/groups/groups.service.ts | user | muss-mandantengebunden | gebunden | Nutzerverwaltung innerhalb eines Mandanten. Seit 260909-jts gebunden; `addUserToDefaultGroup` prueft seither zusaetzlich, dass der Zielbenutzer zum Mandanten gehoert (Befund E, T-JTS-02). Bis 20260910120000_rls_widen_membership_grant_and_platform_read pruefte die Regel auf `GroupMembership` nachweislich nur die Gruppenseite — seit 260910-jab prueft sie beide Seiten, die Anwendungspruefung bleibt trotzdem bestehen (Schalter weiterhin aus, #18). |
| apps/api/src/groups/module-grants.service.ts | group | muss-mandantengebunden | gebunden | Wie groups.service.ts. Seit 260909-jts (Aufgabe 3) gebunden — `assertTargetBelongsToTenant` erzeugt seinen eigenen Kontext, `getMatrix` teilt sich einen Kontext mit den beiden anderen parallelen Teilabfragen. |
| apps/api/src/groups/module-grants.service.ts | groupMembership | muss-mandantengebunden | gebunden | Kein eigenes `tenantId`, RLS über Join auf `Group`. Seit 260909-jts gebunden; der `where`-Filter über die Beziehung zur Gruppe bleibt zusätzlich stehen, weil die Regel auf dieser Tabelle nachweislich nur die Gruppenseite prüft (Aufgabe 1). |
| apps/api/src/groups/module-grants.service.ts | moduleGrant | muss-mandantengebunden | gebunden | Modulfreigaben je Mandant. Seit 260909-jts gebunden; die Mandanten-Gegenprüfung vor jedem Erteilen (`assertTargetBelongsToTenant`) bleibt zusätzlich bestehen, weil die Regel auf dieser Tabelle nur die Mandantenkennung der Zeile prüft, nicht die referenzierte Gruppe (Befund F, T-JTS-03, Aufgabe 1). |
| apps/api/src/groups/module-grants.service.ts | moduleGrant | muss-mandantengebunden | gebunden | Modulfreigaben je Mandant. Seit 260909-jts gebunden; die Mandanten-Gegenprüfung vor jedem Erteilen (`assertTargetBelongsToTenant`) bleibt zusätzlich bestehen. Bis 20260910120000_rls_widen_membership_grant_and_platform_read prüfte die Regel auf dieser Tabelle nur die Mandantenkennung der Zeile, nicht die referenzierte Gruppe/den referenzierten Benutzer (Befund F, T-JTS-03, Aufgabe 1) — seit 260910-jab prüft sie beide Ziele zusätzlich, die Anwendungsprüfung bleibt trotzdem der erste Schutz (Schalter weiterhin aus, #18). |
| apps/api/src/groups/module-grants.service.ts | tenantModuleActivation | muss-mandantengebunden | gebunden | Welche Module ein Mandant aktiviert hat, `tenantId`-Spalte vorhanden. Seit 260909-jts gebunden. |
| apps/api/src/groups/module-grants.service.ts | user | muss-mandantengebunden | gebunden | Zielbenutzer eines Grants innerhalb des Mandanten. Seit 260909-jts gebunden; die Mandanten-Gegenprüfung bleibt bestehen. |
| 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. |
@@ -322,7 +342,7 @@ verwendet), Klasse, Stand (`gebunden`/`ungebunden`/`gemischt`, seit
| apps/api/src/tenders/tender-matching.service.ts | tenderSavedSearch | beides | ungebunden | Gespeicherte Suchprofile ALLER Mandanten werden gegen neue Treffer geprüft — bewusst übergreifend, Etappe-3-Übergabe (260909-laa, Aufgabe 3, unverändert). |
| apps/api/src/tenders/tender-matching.service.ts | user | beides | gebunden | E-Mail-Adressen für die Sofortmeldung. Seit 260909-laa (Aufgabe 3) läuft der einzige Zugriff über `forTenant()`, gebunden an den Mandanten des jeweiligen Profils. |
| apps/api/src/tenders/tender-notification-pref.service.ts | tenderNotificationPref | muss-mandantengebunden | gebunden | Nutzer-CRUD für die eigenen Benachrichtigungseinstellungen — Mandant aus der Anfrage bekannt. Seit 260909-laa (Aufgabe 2) laufen `getForUser`/`setForUser` vollständig über `forTenant()`; `setForUser` übersetzt eine P2002-Verletzung (Befund F) in eine verständliche deutsche Meldung. |
| apps/api/src/tenders/tender-rss-feed.service.ts | tenderRssFeedSource | beides | gemischt | Nutzer-CRUD für die eigenen RSS-Quellen (anders als der Fan-out in `adapters/rss.adapter.ts`). Seit 260909-laa (Aufgabe 2) bindet `createForUser` (Zähler + Anlage, beide ausschließlich auf persönlichen Zeilen mit gesetztem Mandanten) über `forTenant()`; `listForUser`/`createPlatform`/`remove` bleiben bewusst ungebunden, weil sie (auch) die nullbare, plattformweite Zeile berühren (WINDOWS #19, Aufgabe 1 gemessen: eine gebundene Zeile wäre unter jedem Mandanten unsichtbar bzw. ein gebundenes Einfügen ohne Mandant würde abgewiesen). Korrektur der Klasse von `muss-mandantengebunden` auf `beides`, keine Verhaltensänderung — derselbe Präzedenzfall wie `ldapConfig` in 260909-ipc. |
| apps/api/src/tenders/tender-rss-feed.service.ts | tenderRssFeedSource | beides | gemischt | Nutzer-CRUD für die eigenen RSS-Quellen (anders als der Fan-out in `adapters/rss.adapter.ts`). Seit 260909-laa (Aufgabe 2) bindet `createForUser` (Zähler + Anlage, beide ausschließlich auf persönlichen Zeilen mit gesetztem Mandanten) über `forTenant()`. Seit 260910-jab (Aufgabe 2, WINDOWS #19 geschlossen) bindet zusätzlich `listForUser` — die neue Leseregel (`tenant_platform_read_policy`, 20260910120000_rls_widen_membership_grant_and_platform_read) schließt die plattformweite Zeile ausdrücklich ein, ungebunden hätte die Reparatur den Pfad sonst still auf nur die plattformweiten Zeilen reduziert (Befund F). `createPlatform`/`remove` bleiben bewusst ungebunden — beide Pfade lassen sich unter der Anwendungsrolle grundsätzlich nicht anlegen/entfernen, weil jede Schreibregel einen Mandanten verlangt (WINDOWS #24, eigener offener Punkt). Klasse `beides` bleibt korrekt: zwei gebundene, zwei bewusst ungebundene Zugriffe in derselben Datei. |
| apps/api/src/tenders/tender-saved-search.service.ts | tenderSavedSearch | muss-mandantengebunden | gebunden | Nutzer-CRUD für die eigenen gespeicherten Suchprofile — Mandant aus der Anfrage bekannt. Seit 260909-laa (Aufgabe 2) laufen `list`/`create`/`update`/`remove` vollständig über `forTenant()`. |
| 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 | gebunden | Favorisierungs-/Ablehnungsstatus eines Nutzers, `tenantId`-Spalte vorhanden. Seit 260909-laa (Aufgabe 2) laufen `setTriage`/`listForUser`/`favoriteIds` vollständig über `forTenant()`. |
@@ -348,9 +368,13 @@ verwendet), Klasse, Stand (`gebunden`/`ungebunden`/`gemischt`, seit
`forTenant()`- bzw. `withTenantTransaction()`-Aufruf, gebundene Clients
werden nicht zwischen Methoden weitergereicht. Die Frage bleibt für alle
übrigen Bereiche der Etappe 2 offen.
- Wie die WINDOWS-#19-Policy für `SearchProvider`/`TenderRssFeedSource`
- ~~Wie die WINDOWS-#19-Policy für `SearchProvider`/`TenderRssFeedSource`
am Ende genau lautet — nur, dass sie vor dem Scharfschalten gelöst sein
muss.
muss.~~ Aufgelöst (260910-jab): `TenderRssFeedSource` bekommt vier nach
Befehl getrennte Regeln, `SearchProvider` bleibt unverändert streng
(widerlegte Prämisse). Siehe Abschnitt "Zwei belegte Befunde" oben. Was
weiterhin offen ist: der Verwaltungsweg für plattformweite Zeilen unter
der Anwendungsrolle (WINDOWS #24).
- Die Reihenfolge und Zuschnitt der Etappe-2-Pläne — dafür ist die
Klassen-Verteilung oben der Arbeitsvorrat, siehe `<next_stages>` im
Plan `260909-eor-PLAN.md`.