22 KiB
phase, plan, subsystem, tags, requires, provides, affects, actuals, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, coverage, duration, completed, status
| phase | plan | subsystem | tags | requires | provides | affects | actuals | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | coverage | duration | completed | status | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-260911-gwh | 01 | database |
|
|
|
|
|
|
|
|
|
|
|
55min | 2026-09-11 | complete |
Quick 260911-gwh: Etappe 2 der Mandantentrennung, Bereiche favorites und settings — LETZTER Lauf Summary
favorites.service.ts und settings.service.ts vollstaendig an forTenant() gebunden (12 gebundene Zugriffe, 8 Aufrufstellen), der Fremdschluessel-Durchgriff auf WidgetInstance gemessen und mit einem Besitzriegel geschlossen, der Mailmodul-Startpfad als sechster Hintergrunddienst-Fall markiert und ungebunden gelassen — Etappe 2 der Mandantentrennung ist damit vollstaendig: 65 (Datei,Modell)-Paare, 68 ungebunden/178 gebunden, jeder verbleibende ungebundene Rest ist ein benannter, bewusster Fall.
Performance
- Duration: ca. 55 min
- Tasks: 3/3
- Files modified: 11 (2 neu, 9 geaendert)
- Commits: 4 (plus die vorangehende PLAN.md-Ablage)
Accomplishments
apps/api/scripts/rls-scratch-check.mjsvon 120 auf 137 bestandene Pruefungen erweitert (runFavoritesAreaChecks: 8,runSettingsAreaChecks: 9), beide an der Regel WORTGLEICH aus20260909140000_rls_remaining_tenant_tablesgeschnitten, mit dem Fremdschluessel bzw. Eindeutigkeitsindex als mitgebauten Voraussetzungen.favorites.service.ts:list,create,update,remove,getIconByteslaufen je ueber GENAU EINEN KliententenantPrisma(7 gebundenefavoriteLink-Zugriffe, 1 gebundenerwidgetInstance-Besitzriegel increate, 5 Aufrufstellen des Bindungshilfsmittels).settings.service.ts:getSmtpConfig,saveSmtpConfig,getDecryptedSmtpConfiggebunden (3 Zugriffe, 3 Aufrufstellen); Startpfad umbenannt inloadAnySmtpConfigForStartupTransport(), bleibt bewusst ungebunden, sechster Fall der Hintergrunddienst-Falle, WINDOWS #30.- Befund K (Reihenfolgebedingung aus
tenders(t4) unddkv(d4)) ist ERFUELLT und an allen drei Stellen als solches vermerkt: (t4)-Nachtrag, (d4)-Nachtrag, Hintergrunddienst-Abschnitt der Klassifikation. - Zwei neue Testdateien mit dem Zwei-Klienten-Nachbau (23 + 20 = 43 neue Faelle), sechs Falsifizierungsnachweise durchgefuehrt und zurueckgenommen.
- Alle fuenf handgepflegten Dokumentstellen auf den Endstand der Etappe 2 gebracht, DERIVIERT gegatet (
rls-access-inventory.spec.ts). - Drei neue offene Ledger-Eintraege (#30, #31, #32).
Task Commits
- Aufgabe 1: Fehlerrichtung fuer favorites/settings messen —
88896d3(docs) — 137 Pruefungen,## Bereich favorites(f1-f5),## Bereich settings(s1-s5),## Etappe 2 — Abschluss, Nachtraege unter Befund K in (t4)/(d4) - Aufgabe 2, RED: neue Testdateien —
8f2c13a(test) — favorites.service.spec.ts (23 Faelle), settings.service.spec.ts (20 Faelle), beide gegen die heutige Implementierung erwartungsgemaess rot - Aufgabe 2, GREEN: binden, Startpfad umbenennen, Besitzriegel —
b5f22e2(feat) — alle Ziel-Signaturen, vier Falsifizierungsnachweise - Aufgabe 3: Etappe 2 auf Endstand bringen —
1240932(docs) — Ledger #30/#31/#32, Klassifikation, Anleitung, Nachtraege, zwei Dokument-Falsifizierungen
Plan metadata: 2a27d96 (docs: Plan fuer Etappe 2, Bereiche favorites und settings) — bereits vor dieser Ausfuehrung committet (Plan-Checker-Lauf).
Kein REFACTOR-Commit — die GREEN-Implementierung brauchte keine Nacharbeit.
Files Created/Modified
apps/api/src/favorites/favorites.service.spec.ts(NEU) — Zwei-Klienten-Nachbau, 23 Faelleapps/api/src/settings/settings.service.spec.ts(NEU) — Zwei-Klienten-Nachbau mit Grenze als Bauform (ungebunden nurfindFirst, gebunden nurfindUnique/upsert), 20 Faelleapps/api/scripts/rls-scratch-check.mjs—runFavoritesAreaChecks,runSettingsAreaChecksapps/api/src/favorites/favorites.service.ts— Bindung, Besitzriegelapps/api/src/favorites/favorites.controller.ts— reichttenantIddurchapps/api/src/settings/settings.service.ts— Bindung, Startpfad-Umbenennungapps/api/src/mail/mail.module.ts— ruft den umbenannten Startpfaddocs/mandantentrennung-etappe2-fehlerrichtung.md— zwei neue Bereichsabschnitte, Abschluss-Abschnitt, zwei Nachtraegedocs/mandantentrennung-zugriffsklassifikation.md— Uebersicht, Bestandsaufnahme, Klassen-Verteilung, Hintergrunddienst-Abschnittdocs/anleitung-entwicklung.md— RLS-Tabellenzahl und Beispielabsatz auf den gemessenen Stand.planning/WINDOWS.md— drei neue offene Eintraege (#30, #31, #32)
Tatsächlich gezählte Prüfungs- und Testzahlen
Werkzeug (rls-scratch-check.mjs): 120 → 137 bestandene Prüfungen (8 runFavoritesAreaChecks + 9 runSettingsAreaChecks).
Testsuite: Baseline 951 Tests / 60 Dateien (260911-fh9) → RED (Aufgabe 2, Commit 8f2c13a): 994 Tests entdeckt / 62 Dateien, 40 rot (22 favorites + 18 settings), 954 grün — beide RED-Zustände intentional, jeweils auf der geplanten Zielsignatur gescheitert, nicht an Syntax/Zero-Discovery → GREEN (Aufgabe 2, Commit b5f22e2): 43/43 neue Fälle grün, aber rls-access-inventory.spec.ts (Teil der ursprünglichen 951) mit 2 von 11 Fällen erwartungsgemäß rot, 992/994 gesamt grün → Aufgabe 3 (Commit 1240932): 994/994 grün in 62 Dateien.
Zwischenzeitlich rot: rls-access-inventory.spec.ts, zwischen Aufgabe 2 und Aufgabe 3. Genau wie das Aufgabe-3-Actionblock des Plans selbst vorhersagt ("Ohne Schritt 3 ist rls-access-inventory.spec.ts am Ende dieser Aufgabe rot") und wie der unmittelbare Vorgänger 260911-fh9 es bereits dokumentiert hat: sobald favorites.service.ts/settings.service.ts ihre Stand-Spalte änderten (ungebunden → gebunden/gemischt) und favorites.service.ts/widgetInstance als neue Fundstelle entstand, maß die Prüfung diese drei Fakten sofort — während das Klassifikationsdokument sie erst in Aufgabe 3 nachzieht. Zwei der elf Fälle scheiterten entsprechend (jede ... Fundstelle ist im Dokument eingetragen wegen der neuen widgetInstance-Zeile, der eingetragene Stand stimmt ... überein wegen der beiden Stand-Wechsel). Nicht als Blocker gewertet: (a) exakt die im Plan selbst vorausgesagte Form, (b) berührte keine Datei außerhalb der sechs für Aufgabe 2 erlaubten, (c) Aufgabe 3 folgte im selben Lauf und stellte die Baseline innerhalb von Minuten wieder her (994/994). "Baseline gehalten nach jeder Aufgabe" ist deshalb — wie schon bei 260911-fh9 — als "nach dem vollständigen Plan, mit einem im Plan selbst vorausgesagten Zwischenzustand" zu lesen, nicht als literarische Bedingung jedes einzelnen Aufgaben-<verify>-Blocks für sich.
Ergebnis von Prüfung 7 (Fremdschlüssel) — wörtlich
favoritelink-fremdschluessel-prueft-am-zeilenschutz-vorbei: ein gebundenes create unter TENANT-A mit widgetId='widget-b1' (gehört TENANT-B, unter TENANT-A per gebundenem widgetInstance.findUnique unsichtbar: null) GELINGT (id=fav-a1-fremdes-widget) — der Fremdschlüssel prüft am Zeilenschutz VORBEI, dokumentiertes PostgreSQL-Verhalten. Dasselbe create mit widgetId="widget-gibt-es-nicht" scheitert mit PrismaClientKnownRequestError (code P2003): Foreign key constraint violated on the constraint: FavoriteLink_widgetId_fkey. Ergebnis: der Besitzriegel in Aufgabe 2 war NÖTIG (nicht optional) — ohne ihn wäre der Unterschied zwischen beiden Antworten ein Existenzorakel über Mandantengrenzen gewesen (T-GWH-05).
Ergebnis von Prüfung 8 (Konfliktform) — wörtlich
smtpconfig-ungebundenes-upsert-auf-unsichtbare-zeile-scheitert-laut: ungebundenes prisma.smtpConfig.upsert({ where: { tenantId: 'TENANT-A' }, ... }) (die Form von saveSmtpConfig) wirft PrismaClientUnknownRequestError: ConnectorError(... PostgresError { code: "42501", message: "new row violates row-level security policy for table \"SmtpConfig\"" ... }) — dieselbe Fehlerklasse wie die 260910-krx-Messung für DashboardLayout (NICHT PrismaClientKnownRequestError/P2002, die Form von tenders/user). Die Regel weist den Schreibzugriff ab, bevor der Eindeutigkeitsindex überhaupt geprüft wird.
Alle sechs Falsifizierungsnachweise — Testname und Meldung wörtlich
(a) Aufgabe 2 — list probeweise auf den ungebundenen Basisclient zurückgebaut (const tenantPrisma = this.prisma as any;): 3 Fälle rot.
list > liefert nur die Zeilen von user-a1 für widget-a1, sortiert nach position, dann title—TypeError: Cannot read properties of undefined (reading 'findMany')list > liefert unter einem FREMDEN Mandanten eine leere Liste, kein Fehler ...— dieselbeTypeErrorWachhund je Methode > genau EIN gebundener Klient je Aufruf von list/update/remove/getIconBytes—AssertionError: Aufruf erzeugte 0 gebundene Klienten, erwartet genau 1: expected +0 to be 1
(b) Aufgabe 2 — Widget-Besitzriegel in create probeweise entfernt. Der Plan sagte "genau die drei Widget not found-Fälle" voraus — GEMESSEN sind es 4, weil der Wachhund-Fall zusätzlich rot wird (Abweichung, siehe unten):
create > T-GWH-05: widgetId gehört einem ANDEREN Benutzer desselben Mandanten -> NotFoundException "Widget not found", KEIN create, KEINE Icon-Suche—AssertionError: promise resolved "{ …(10) }" instead of rejectingcreate > T-GWH-05: widgetId gehört einem Widget unter FREMDEM Mandanten -> dieselbe NotFoundException, nennt weder Halter noch Mandant— dieselbeAssertionError-Formcreate > T-GWH-05: unbekannte widgetId -> dieselbe NotFoundException— dieselbe Formcreate > Wachhund: genau EIN gebundener Klient je create-Aufruf, Widget-Prüfung UND Schreibzugriff auf DEMSELBEN Klienten—AssertionError: expected [ { tenantId: 't1', …(2) } ] to deeply equal [ { tenantId: 't1', …(2) }, …(1) ]
(c) Aufgabe 2 — getDecryptedSmtpConfig probeweise auf den ungebundenen Basisclient verschoben: 9 Fälle rot, alle mit TypeError: tenantPrisma.smtpConfig.findUnique is not a function — betrifft die drei getDecryptedSmtpConfig-Fälle, alle vier testSmtpConfig-Fälle (ruft intern getDecryptedSmtpConfig auf) und beide betroffenen Wachhund-Fälle.
(d) Aufgabe 2 — Startpfad probeweise gebunden (forTenant(this.prisma, 'falsification-probe').smtpConfig.findFirst()): 4 Fälle rot, alle mit TypeError: (0 , forTenant)(...).smtpConfig.findFirst is not a function — beide Erfolgsfälle, der Leer-Nachbau-Fall und der Null-Klienten-Nachweis.
(e) Aufgabe 3 — Bestandsaufnahme-Zeile favorites.service.ts | favoriteLink probeweise auf ungebunden zurückgesetzt: rls-access-inventory.spec.ts > ... > der eingetragene Stand stimmt mit dem im Quelltext gemessenen überein — AssertionError: Abweichender Stand (Dokument vs. Quelltext): apps/api/src/favorites/favorites.service.ts::favoriteLink — dokumentiert=ungebunden, gemessen=gebunden.
(f) Aufgabe 3 — Übersichtszeile settings probeweise auf 9 | 9 gesetzt: das herleitende Gate (grep -qE "^\| settings \| ${SU} \| ${SB} \| ..." mit den tatsächlich gemessenen SU=1/SB=3) schlägt fehl — die Zeile 9 | 9 matcht die Anweisung nicht mehr.
Alle sechs Änderungen wurden unmittelbar nach der Messung zurückgenommen; diff gegen den vor der Probe gesicherten Stand bestätigt Identität in jedem Fall.
Decisions Made
- Startpfad-Ledger-Eintrag (#30) eigenständig, nicht an WINDOWS #21 angeschlossen: andere Datei, andere Reparatur, andere Verdeckungsform — siehe
key-decisionsoben. - Widget-Besitzriegel in
create()gebaut, weil Prüfung 7 (Aufgabe 1) das Gelingen des Fremdschlüssel-Durchgriffs tatsächlich gemessen hat (nicht angenommen). settings.controller.tsundfavorites.controller.tssextractContextbleiben unverändert — beide Mandantenquellen sind bereits die richtigen (siehekey-decisions).
Deviations from Plan
Auto-fixed / gemessene Abweichungen (keine Rule-1/2/3-Bugfixes — alles Messungen, die anders ausfielen als die Planungsvermutung)
1. Falsifizierungsnachweis (b): 4 statt 3 rote Fälle.
- Gefunden während: Aufgabe 2, TEIL 5.
- Planungsvermutung: "genau die drei
Widget not found-Fälle werden rot". - Tatsächliche Messung: zusätzlich der Wachhund-Fall (
create > Wachhund: ...), weil er das Bindungsprotokoll auf zwei Einträge (widgetInstance.findUnique,favoriteLink.create) prüft — ohne den Riegel gibt es nur den zweiten Eintrag. - Auswirkung: keine — die Falsifizierung bestätigt weiterhin, dass der Riegel notwendig ist; die Zahl ist hier korrigiert, nicht die Planungsaussage stillschweigend übernommen.
2. (d4)-Nachtrag: dkv.seed.ts/module-registry ist NICHT "gebunden seit 260910-exd".
- Gefunden während: Aufgabe 3, TEIL 3, Nachtrag unter (d4).
- Planungstext: "
dkv.seed.ts/module-registryist seit 260910-exd gebunden — prüfen und, falls zutreffend, in demselben Nachtrag mit einem Satz nennen." - Tatsächliche Messung:
dkv.seed.tsruftModuleRegistryService.seedModule()(module-registry.service.ts:206,this.prisma.module.upsert) — UNGEBUNDEN, bewusst und unverändert, weilModuleder plattformweite Modulkatalog ohnetenantId-Spalte ist (Befund E). Der Nachtrag in (d4) sagt das ausdrücklich, statt die Planungsvermutung zu übernehmen.
3. Zwischenzeitlich rotes rls-access-inventory.spec.ts zwischen Aufgabe 2 und Aufgabe 3 — siehe eigener Abschnitt oben ("Tatsächlich gezählte Prüfungs- und Testzahlen"). Vom Plan selbst vorausgesagt, kein Bug.
Total deviations: 3, alle Messergebnisse (keine Bugfixes, keine Scope-Erweiterung). Kein Rule-1/2/3-Autofix in diesem Lauf nötig. Impact on plan: keiner — der Plan bleibt in Kraft, alle drei Punkte sind Präzisierungen der eigenen Planungsvermutungen anhand der tatsächlichen Messung, wie es der Plan selbst an mehreren Stellen verlangt ("weicht eine Messung ab, gilt die Messung").
Issues Encountered
Keine. Alle Prüfungen liefen beim ersten Durchlauf durch (rls-scratch-check.mjs: 137/137 ohne Nacharbeit).
Ledger-Einträge und Entscheidung zu #21
Drei neue offene Einträge in .planning/WINDOWS.md:
- #30 (
apps/api/src/mail/mail.module.ts, deviation) — Startpfad des Mailmoduls, sechster Fall der Hintergrunddienst-Falle. Entscheidung: EIGENER Eintrag, NICHT an #21 angeschlossen — Grund: andere Datei (mail.module.ts/settings.service.tsstattdkv-scheduler.service.ts), andere Reparatur (Transport je Versand ausgetDecryptedSmtpConfig(tenantId)statt Mehrmandanten-Planung), andere Verdeckungsform (Rückfallkette auf einen falschen, aber vorhandenen Transport statt bloßer Leere mit Protokollzeile). - #31 (
apps/web/src/components/dashboard/widgets/favorites-widget.tsx, deviation) — verschluckte Leerefavorites, Familie #23/#25/#26/#28. - #32 (
apps/web/src/components/settings/smtp-settings-form.tsx, deviation) — verschluckte Leeresettings, dieselbe 200-leerer-Rumpf-Kette wie #28.
Kopfzähler geprüft: open_count: 14, total_count: 32, Tabellenzeilen = 32, offene Zeilen = 14 — beide stimmen.
Endstand der Etappe 2 (aus dem Abschluss-Abschnitt der Kritikschrift, nicht neu gerechnet)
- Zwölf Bereichs-/Regel-Läufe von 260909-ipc bis 260911-gwh.
- Übersichtstabelle: 68 ungebundene / 178 gebundene Rohtreffer (Summe 246; zur Erinnerung: der ursprüngliche Kopf des Klassifikationsdokuments nannte 227 Rohtreffer über 59 Paare — die höhere Summe stammt vom neuen, zur Planungszeit noch nicht feststehenden
widgetInstance-Besitzriegel). - Klassen-Verteilung: 65 (Datei,Modell)-Paare — 33
muss-mandantengebunden, 17keine-mandantengebundene-tabelle, 13beides, 2bewusst-uebergreifend. - Werkzeug: 137/137 Prüfungen bestanden (
rls-scratch-check.mjs). - Tests: 994/994 grün in 62 Dateien.
- Jeder verbleibende ungebundene Rohtreffer ist einer der in
docs/mandantentrennung-etappe2-fehlerrichtung.mdbzw.docs/mandantentrennung-zugriffsklassifikation.mdnamentlich benannten, bewusst ungebundenen Fälle — keiner ist übersehen. - Schalter bleibt AUS (
DATABASE_URLunverändert auf Rolletessera), Schema/Migrationen/Compose/Umgebungsdateien unangetastet, Erlaubnisliste gegen46f0e78gehalten.
User Setup Required
None — keine externe Konfiguration nötig.
Next Phase Readiness
Etappe 2 ist mit diesem Lauf abgeschlossen. Was für Etappe 3 bleibt (siehe Abschluss-Abschnitt der Kritikschrift):
- Anmeldeweg unter je Mandant eindeutigen Anmeldenamen (
username/email). - Benutzerdimension der Regeln (mehrere Bereiche kennen sie nicht —
FavoriteLinkeingeschlossen). - Modulkatalog-Regel für
Module(Befund E), falls Etappe 3 sie einführt. - Kennzeichnung der
bewusst-uebergreifend-Stellen (Systemkontext). - Mandantenwechsel im Ausschreibungs-Digest.
Etappe 4 (rls-preflight.mjs) muss vor dem Scharfschalten die in den Bereichsabschnitten benannten Vorabprüfungen laufen lassen (Liste im Abschluss-Abschnitt der Kritikschrift) — die Befund-K-Bedingung ist davon jetzt ausgenommen, weil sie erfüllt ist.
Phase: quick-260911-gwh Completed: 2026-09-11
Self-Check: PASSED
All 12 created/modified files confirmed present on disk; all 4 task commits (88896d3, 8f2c13a, b5f22e2, 1240932) confirmed in git log.