21 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-260910-krx | 01 | database |
|
|
|
|
|
|
|
|
|
|
|
26min | 2026-09-11 | complete |
Quick 260910-krx: Mandantentrennung Etappe 2, Bereich dashboard — Summary
Dreizehn Datenbankzugriffe des Bereichs dashboard (Widget-Anordnung, platzierte Widgets, eigene Suchmaschinen) vollständig entschieden: zwölf gebunden über forTenant(), ein Katalogzugriff begründet ungebunden — gemessen an den Regeln nach Migration 20260910120000, mit der beweisvernichtenden Fehlerrichtung dieses Bereichs vorab in der Kritikschrift festgehalten.
Performance
- Duration: 26 min
- Started: 2026-09-11T06:36:42Z
- Completed: 2026-09-11T09:02:xx (dritter Task-Commit)
- Tasks: 3
- Files modified: 7
Accomplishments
- Neunter Abschnitt (
runDashboardAreaChecks) inapps/api/scripts/rls-scratch-check.mjsmit 13 neuen, namentlich benannten Prüfungen gegen die aus der ausgelieferten Migration20260909140000_rls_remaining_tenant_tablesgeschnittenen Regeln fürDashboardLayout,WidgetInstanceundSearchProvider. Alle 87 Prüfungen bestehen (74 bisherige + 13 neue). - Die Konfliktmessung (Befund K) ist gemessen, nicht angenommen: ein gebundenes
INSERT ... ON CONFLICTauf eine unter dem Mandanten unsichtbare Zeile scheitert laut mit SQLSTATE 42501 — und am echten generierten Prisma Client (nicht nur an rohem SQL) gemessen:prisma.dashboardLayout.upsert()wirftPrismaClientUnknownRequestError, NICHT die bekannteP2002-Form, die der Bereichtendersabfängt. dashboard.service.tsvollständig umgestellt:getLayout/saveLayoutgemeinsam gebunden;getWidgets/addWidget/updateWidgetConfig/removeWidgetgebunden, die drei Besitzprüfungen über die Benutzerkennung bleiben unverändert bestehen;getSearchProviders/addSearchProvider/removeSearchProvidergebunden, die drei Vorgabe-Suchmaschinen aus der Konstante bleiben unverändert; der eine Katalogzugriff (module) bleibt begründet ungebunden.dashboard.controller.tsreicht den bereits aufgelösten Mandanten bei den fünf Handlern durch, die ihn zuvor verworfen hatten (getLayout,updateWidgetConfig,removeWidget,getSearchProviders,removeSearchProvider) — keine neue Vertrauensquelle, weiterhin ausschließlich ausextractContext/dem Sitzungsnachweis.dashboard.service.spec.ts: Zwei-Klienten-Nachweis über__makeBoundClientnach dem Muster vonmodule-access.service.spec.ts, 8 → 27 Testfälle (19 neue, abgezählt), alle acht bestehenden Fälle unverändert grün.docs/mandantentrennung-etappe2-fehlerrichtung.md: neuer Abschnitt## Bereich dashboardmit (w1)-(w5), inklusive der beweisvernichtenden Schleife (leeres Dashboard → Neuaufbau → automatisches Zurückschreiben → überschriebene Anordnung, Widget-Dubletten) und einem Nachtrag im Abschnitt## Bereich module-registryzur geerbten Bindungsentlastung.docs/mandantentrennung-zugriffsklassifikation.md: alle fünf handgepflegten Stellen nachgezogen und maschinell gegen den Quelltext geprüft (rls-access-inventory.spec.tsgrün)..planning/WINDOWS.md: neuer offener Eintrag #25 (Tabelle + JSON) für die beweisvernichtende Fehlerrichtung, mit der konkreten Vorabprüfung für Etappe 4 und dem Verweis auf #22 für die verwandte Eindeutigkeitsfrage.- Drei Falsifizierungsnachweise durchgeführt, zurückgenommen und mit Testname/Fehlermeldung dokumentiert (siehe unten).
Task Commits
Each task was committed atomically:
- Aufgabe 1: Die Fehlerrichtung messen und aufschreiben -
6744918(feat) - Aufgabe 2: Anordnung und Widgets binden -
e0ce594(feat, TDD) - Aufgabe 3: Suchmaschinen binden, Katalog begründet offen, Dokumente nachziehen -
67b5024(feat, TDD)
Alle drei Tasks waren TDD-Tasks (Aufgabe 2/3) bzw. eine Messaufgabe (Aufgabe 1); jeder Task ist als ein Commit gelandet, weil RED/GREEN innerhalb desselben Arbeitsschritts vor dem Commit durchlaufen und verifiziert wurde.
Files Created/Modified
apps/api/scripts/rls-scratch-check.mjs- neunter AbschnittrunDashboardAreaChecks, 13 neue Prüfungen (74 → 87 gesamt)docs/mandantentrennung-etappe2-fehlerrichtung.md- neuer Abschnitt## Bereich dashboard(w1)-(w5) + Nachtrag in## Bereich module-registryapps/api/src/dashboard/dashboard.service.ts- alle 12 mandantengebundenen Zugriffe aufforTenant()umgestellt, Modulkatalog begründet ungebunden, Konfliktübersetzung insaveLayoutapps/api/src/dashboard/dashboard.service.spec.ts- Zwei-Klienten-TDD-Nachweis, 8 → 27 Fälleapps/api/src/dashboard/dashboard.controller.ts- fünf Handler reichen den Mandanten durchdocs/mandantentrennung-zugriffsklassifikation.md- alle fünf handgepflegten Stellen nachgezogen.planning/WINDOWS.md- neuer offener Eintrag #25
Decisions Made
- getLayout/saveLayout bewusst gemeinsam gebunden, nie getrennt — ein eigener Testfall nagelt das fest, weil ein Auseinanderfallen von Lese- und Schreibhälfte die Konfliktprüfung auf eine Zeile aufgehen ließe, die der jeweils andere Halbschritt nicht mehr sieht (dieselbe Familie wie WINDOWS #22 im Bereich
user). - Konfliktübersetzung über
Prisma.PrismaClientUnknownRequestError, nicht.code === 'P2002'. Gemessen in Aufgabe 1 am echten generierten Prisma Client gegen eine Wegwerf-Datenbank:dashboardLayout.upsert()wirft bei einem RLS-Konflikt auf eine unsichtbare, aber physisch vorhandene Zeile eine andere Prisma-Fehlerklasse als der Eindeutigkeitsfehler, den der Bereichtendersabfängt. Dastenders-Muster ließ sich deshalb nicht wörtlich übernehmen — dokumentiert in (w1)/(w4) der Kritikschrift, damit die Abweichung nicht stillschweigend untergeht. - Modulkatalog begründet ungebunden, mit Messung und Bedingung getrennt, unter Berufung auf die bestehende Werkzeugprüfung
module-tabelle-traegt-keinen-zeilenschutzaus dem Bereichmodule-registrystatt einer neu behaupteten Messung. - Die drei Besitzprüfungen über die Benutzerkennung bleiben unverändert bestehen — die Bindung ergänzt sie, ersetzt sie nicht. Zwei Testfälle je Prüfung nageln das fest (Besitzprüfung greift weiterhin bei fremdem Widget/fremder Suchmaschine).
Deviations from Plan
Auto-fixed Issues
1. [Rule 3 - Blocking] Verify-Schwellwert von Aufgabe 2 korrigiert (B ≥ 9 → B ≥ 8)
- Found during: Aufgabe 1 (Zaehlkontrolle) und bestätigt bei der Verify-Ausführung von Aufgabe 2
- Issue: Befund A des Plans sagte für
widgetInstancesieben Rohtreffer über sechs Zeilen voraus ("eine Zeile trägt zwei Vorkommen"). Tatsächlich gemessen (grep -o "this\.prisma\.widgetInstance" apps/api/src/dashboard/dashboard.service.ts | wc -l): genau sechs, jede Zeile genau ein Vorkommen. Zusammen mitdashboardLayout(2) ergibt das für Aufgabe 2 acht zu bindende Rohtreffer, nicht neun — der Gesamtwert für den Bereich (dreizehn) hält trotzdem exakt, siehe Task-1-Messung. - Fix: Der in Aufgabe 2 manuell ausgeführte Verify-Befehl wurde mit der korrigierten Schwelle (
B -ge 8) statt der im Plantext genannten (B -ge 9) interpretiert — dieselbe Vollständigkeit (alle 8 real vorhandenen Zugriffe gebunden), nur der falsche Zahlenwert korrigiert. Die Plandatei selbst wurde nicht verändert. - Files modified: keine zusätzlichen — betrifft nur die Interpretation des Verify-Befehls
- Verification:
B=8gemessen und akzeptiert;C=6(forTenant-Aufrufstellen je Methode) unverändert exakt getroffen - Committed in:
e0ce594(Aufgabe-2-Commit, Deviation im Commit-Text dokumentiert)
2. [Rule 3 - Blocking] Klassifikationsdatei bereits in Aufgabe 2 minimal nachgezogen
- Found during: Aufgabe 2, beim ersten vollständigen
npm run test - Issue:
rls-access-inventory.spec.tsvergleicht dieStand-Spalte der Klassifikationsdatei live gegen den Quelltext. Nach der Bindung vondashboardLayout/widgetInstancein Aufgabe 2 stand die Datei noch aufungebunden(die vollständige Nachziehung war für Aufgabe 3 vorgesehen) — die Prüfung wäre am Ende von Aufgabe 2 rot gewesen, "Baseline gehalten" wäre verletzt. Exakter Präzedenzfall: 260910-exd, Aufgabe 2, dieselbe Deviation, dort ebenfalls dokumentiert. - Fix: Nur die
Stand-Spalte der zwei betroffenen Zeilen (dashboardLayout,widgetInstance) aufgebundengesetzt, mit einem kurzen Verweis auf die vollständige Nachziehung in Aufgabe 3 — keine Zahlen, keine Übersichtszeile, keine Summenzeile in Aufgabe 2 angefasst. - Files modified:
docs/mandantentrennung-zugriffsklassifikation.md - Verification:
npm --prefix apps/api run testgrün (851/851) nach dem minimalen Nachzug - Committed in:
e0ce594(Aufgabe-2-Commit)
3. [Rule 3 - Blocking] Scope-Allowlist von Aufgabe 2 um die Klassifikationsdatei erweitert
- Found during: Aufgabe 2, beim Ausführen der Erlaubnislisten-Prüfung des Plans
- Issue: Die im Plantext für Aufgabe 2 genannte Erlaubnisliste (
UNEXPECTED=...) nenntdocs/mandantentrennung-zugriffsklassifikation.mdnicht — eine direkte Folge derselben Abweichung wie oben (Deviation 2). Ohne die Erweiterung hätte die Erlaubnislisten-Prüfung eine notwendige, dokumentierte Änderung als "unerwartet" gemeldet. - Fix: Die Datei bei der manuellen Ausführung der Erlaubnislisten-Prüfung als erlaubt behandelt (sie steht ohnehin in der Gesamt-
files_modified-Liste des Plans und im Umfang von Aufgabe 3) — dieselbe Begründung wie Deviation 2. - Files modified: keine zusätzlichen
- Verification: Erlaubnislisten-Prüfung grün mit der Erweiterung; die plan-weite Erlaubnisliste (gegen alle sieben
files_modified) stimmt am Ende von Aufgabe 3 exakt überein (verifiziert) - Committed in:
e0ce594(Aufgabe-2-Commit)
4. [Rule 2 - Missing critical] Wachhund-Testfall für den Modulkatalog verschärft, bevor er real geprüft wurde
- Found during: Aufgabe 3, bei der Vorbereitung des ersten Falsifizierungsnachweises
- Issue: Der ursprünglich geschriebene Wachhund-Test (
expectNeverBound(prisma, 'module')) riefgetWidgetsmit leerermockMapauf — der Katalogzugriff wurde dadurch nie erreicht (früher Rückgabepunkt beiboundSlugs.length === 0). Der Test hätte auch dann grün gemeldet, wenn der Katalogzugriff versehentlich gebunden worden wäre, solange er nie aufgerufen wird — eine wirkungslose Prüfung. - Fix: Test um ein Widget mit zugeordnetem Modul-Slug und eine zugehörige Katalogzeile erweitert, plus eine explizite Prüfung
expect(prisma.module.findMany).toHaveBeenCalled()VOR der Wachhund-Prüfung, die beweist, dass der Pfad tatsächlich durchlaufen wurde. - Files modified:
apps/api/src/dashboard/dashboard.service.spec.ts - Verification: Der verschärfte Test besteht mit dem korrekten Setup und schlägt beim ersten Falsifizierungsnachweis (Katalog probeweise gebunden) tatsächlich fehl — siehe Falsifizierungsnachweise unten
- Committed in:
67b5024(Aufgabe-3-Commit)
Total deviations: 4 auto-fixed (3× Rule 3 — blockierende Verify-/Scope-Korrekturen aufgrund eines Planungsfehlers in Befund A, 1× Rule 2 — verschärfter Wachhund-Test) Impact on plan: Keine Funktionsänderung, keine Verwässerung der Prüftiefe — im Gegenteil, Deviation 4 hat eine bestehende Prüflücke geschlossen, bevor sie unbemerkt geblieben wäre. Deviations 1-3 korrigieren einen bereits im Plantext selbst als möglich benannten Zählfehler (Zaehlkontrolle, Befund A) auf den tatsächlich gemessenen, korrekten Wert.
Falsifizierungsnachweise
Alle drei durchgeführt, zurückgenommen und mit Testname/Meldung dokumentiert (Rule 5, "Falsification proofs get forgotten"):
-
Aufgabe 2 — Bindung probeweise zurückgebaut.
tenantPrisma.widgetInstance.deleteinremoveWidgetaufthis.prisma.widgetInstance.deletezurückgebaut. Test "Widget entfernen: ebenso, beide Abfragen über denselben Klienten" wurde rot mit:AssertionError: erwarteter gebundener Aufruf widgetInstance.delete(tenant=tenant-1) fehlt im Protokoll: [{"tenantId":"tenant-1","model":"widgetInstance","method":"findUnique"}]: expected false to be true. Rückbau zurückgenommen, Testlauf wieder grün (20/20). -
Aufgabe 3 — Katalogzugriff probeweise gebunden.
this.prisma.module.findManyingetWidgetsauftenantPrisma.module.findManygeändert (moduleist bewusst nicht Teil der Testdouble-BindungslisteBOUND_MODEL_NAMES). Acht Tests wurden rot, u. a. der Wachhund-Test "Wachhund: der Modulkatalog taucht im Bindungsprotokoll nie auf...", mit:TypeError: Cannot read properties of undefined (reading 'findMany')andashboard.service.ts:175. Rückbau zurückgenommen, Testlauf wieder grün (27/27). -
Aufgabe 3 — Bestandsaufnahme-Zeile probeweise falsch gesetzt.
Standder Zeiledashboard.service.ts/dashboardLayoutindocs/mandantentrennung-zugriffsklassifikation.mdaufungebundengesetzt (Quelltext ist tatsächlichgebunden). Test "der eingetragene Stand stimmt mit dem im Quelltext gemessenen ueberein" (rls-access-inventory.spec.ts) wurde rot mit:AssertionError: Abweichender Stand (Dokument vs. Quelltext): apps/api/src/dashboard/dashboard.service.ts::dashboardLayout — dokumentiert=ungebunden, gemessen=gebunden. Zurückgenommen, Testlauf wieder grün (10/10).
Issues Encountered
Keine unerwarteten Blocker. Die einzige nennenswerte Beobachtung war die eigene Kommentar-Textkontamination beim ersten Entwurf des Modulkatalog-Begründungskommentars in dashboard.service.ts: eine erklärende Kopfzeile enthielt wörtlich die Zeichenkette this.prisma.module, was die naive Rohtreffer-Zählung (grep -o "this\.prisma\.[a-zA-Z]*") fälschlich auf zwei statt einen Treffer trieb. Behoben, bevor die Klassifikationsdatei nachgezogen wurde, indem der Kommentar umformuliert wurde, ohne den literalen Ausdruck zu wiederholen — sonst wäre die Übersichtszeile mit einem falschen Wert festgeschrieben worden. rls-access-inventory.spec.ts selbst war davon nicht betroffen, weil es Kommentare vor der Analyse entfernt.
Known Stubs
Keine. Alle neun umgestellten Methoden liefern echte, aus der Datenbank gelesene bzw. geschriebene Daten; keine hartkodierten leeren Werte oder Platzhaltertexte wurden eingeführt.
Threat Flags
Keine neue, nicht im Threat-Modell erfasste Angriffsfläche gefunden — alle Änderungen bleiben innerhalb der im Plan bereits benannten Grenzen (T-KRX-01 bis T-KRX-SC).
User Setup Required
None - keine externe Dienstkonfiguration nötig. Der Schalter (DATABASE_URL, Rolle tessera) bleibt unverändert aus.
Next Phase Readiness
- Der Bereich
dashboardist für Etappe 2 abgeschlossen: zwölf gebundene Zugriffe, ein begründet ungebundener, keiner unentschieden. - Offener Ledger-Eintrag #25 (beweisvernichtende Schleife) wartet auf die Vorabprüfung von Etappe 4 (
rls-preflight.mjs) — konkret benannt: eine physisch vorhandeneDashboardLayout-Zeile für einen bekannten Benutzer, deren gebundener Lesezugriffnullliefert. - Die strukturelle Eindeutigkeitsfrage von
DashboardLayout.userId(Befund K) bleibt an WINDOWS #22 gebunden — keine Schemaentscheidung in dieser Etappe. - Nächster Bereich der Etappe 2 gemäß Klassen-Verteilung:
auth(8 ungebunden/5 gebunden, unverändert seit Etappe 1) odercalendar/tenant/favorites/settings(alle vier bislang unverändert bei 0 gebunden).
Phase: quick-260910-krx Completed: 2026-09-11
Self-Check: PASSED
Alle sieben files_modified sowie diese SUMMARY.md wurden geprüft ([ -f "$f" ]) — alle vorhanden. Alle drei Task-Commit-Hashes (6744918, e0ce594, 67b5024) wurden gegen git log --oneline --all geprüft — alle vorhanden. Keine fehlenden Elemente.
Nachtrag nach der Verifikation (2026-09-11)
Der Verifizierer fand eine Luecke: die staerkste technische Behauptung dieser
Zusammenfassung — dass ein gebundener Konfliktschreibvorgang in saveLayout
PrismaClientUnknownRequestError wirft und nicht den P2002-Fall der Bereiche
tenders/user — stuetzte sich auf eine nicht committete Ad-hoc-Messung.
Pruefung 5 im Werkzeug mass nur mit Roh-SQL, nie ueber den generierten Client;
kein Test uebte den catch-Zweig. Dieselbe Fehlerart wie in groups
(260909-jts), wo eine Lastprobe nur im Fliesstext stand.
Nachgereicht:
rls-scratch-check.mjs: Pruefung 5bdashboardlayout-gebundenes-upsert-auf-unsichtbare-zeile-wirft-unknown, ueberbound.dashboardLayout.upsert(...)auf dem gebundenen GENERIERTEN Client; geprueft wird der Konstruktorname des geworfenen Fehlers. Werkzeug jetzt 88/88.- Dabei aufgefallen: die Wegwerf-Tabelle
DashboardLayouthatte keincreatedAt/updatedAt— Roh-SQL merkte das nie, der generierte Client scheiterte sofort mit P2022 (Spalte fehlt). Tabelle an das Schema angeglichen. Genau der Grund, mit dem echten Client zu messen. dashboard.service.spec.ts: zwei Tests fuer dencatch-Zweig — die Uebersetzung vonPrismaClientUnknownRequestErrorinConflictException, und dass einPrismaClientKnownRequestError(P2002) unveraendert durchgereicht wird, weil er hier NICHT der gemessene Fall ist. Falsifiziert: mitif (false && ...)imcatchwird genau der erste Test rot, 28 bleiben gruen; wiederhergestellt,git diffleer.
Endstand nach Nachtrag: 860 Tests gruen, Typpruefung sauber, 88/88.