248 lines
21 KiB
Markdown
248 lines
21 KiB
Markdown
---
|
||
phase: quick-260910-krx
|
||
plan: 01
|
||
subsystem: database
|
||
tags: [prisma, postgresql, rls, multi-tenancy, nestjs, vitest]
|
||
|
||
requires:
|
||
- phase: quick-260910-jab
|
||
provides: die drei geschlossenen Datenbankregeln (GroupMembership, ModuleGrant, TenderRssFeedSource) und den unveraendert strengen SearchProvider-Regelstand, gemessen in Migration 20260910120000
|
||
- phase: quick-260910-exd
|
||
provides: die bereits gebundene Modul-Zugriffsaufloesung (module-access.service.ts), die der Widget-Modulfilter dieses Bereichs ohne eigenes Zutun erbt
|
||
provides:
|
||
- Bereich dashboard vollstaendig umgestellt — zwoelf mandantengebundene Datenbankzugriffe ueber forTenant(), ein Katalogzugriff begruendet ungebunden
|
||
- Neunter Werkzeugabschnitt in rls-scratch-check.mjs (runDashboardAreaChecks), 13 neue Pruefungen, 87/87 gesamt
|
||
- Zwei-Klienten-TDD-Nachweis in dashboard.service.spec.ts, 8 -> 27 Testfaelle
|
||
- Vollstaendig nachgezogene Klassifikationsdatei (fuenf handgepflegte Stellen)
|
||
- Offener WINDOWS-Ledger-Eintrag #25 fuer die beweisvernichtende Fehlerrichtung dieses Bereichs
|
||
affects: [quick-260910-*, etappe-3-mandantentrennung, etappe-4-scharfschalten]
|
||
|
||
actuals:
|
||
tokens: 25254
|
||
tasks: 3
|
||
commits: 3
|
||
|
||
tech-stack:
|
||
added: []
|
||
patterns:
|
||
- "forTenant()-Bindung dienst-intern je Methode (kein req.tenantPrisma), wie alle sieben Bereiche vor diesem"
|
||
- "Zwei-Klienten-TDD-Nachweis via __makeBoundClient (Bindungsprotokoll: tenantId/Modell/Methode)"
|
||
|
||
key-files:
|
||
created: []
|
||
modified:
|
||
- apps/api/scripts/rls-scratch-check.mjs
|
||
- docs/mandantentrennung-etappe2-fehlerrichtung.md
|
||
- apps/api/src/dashboard/dashboard.service.ts
|
||
- apps/api/src/dashboard/dashboard.service.spec.ts
|
||
- apps/api/src/dashboard/dashboard.controller.ts
|
||
- docs/mandantentrennung-zugriffsklassifikation.md
|
||
- .planning/WINDOWS.md
|
||
|
||
key-decisions:
|
||
- "getLayout/saveLayout binden GEMEINSAM ueber denselben Klienten (Testfall festgenagelt) — nie getrennt auf gebunden/ungebunden, sonst geht die Konfliktpruefung gegen eine Zeile auf, die der Schreibzugriff nicht mehr sieht"
|
||
- "saveLayout uebersetzt eine PrismaClientUnknownRequestError (RLS-Konflikt) in eine deutsche ConflictException — NICHT das tenders-P2002-Muster, weil die gemessene Fehlerklasse eine andere ist"
|
||
- "Modulkatalog bleibt begruendet ungebunden, mit Messung/Bedingung getrennt, unter Berufung auf die bestehende module-registry-Pruefung statt einer neuen Behauptung"
|
||
- "Die drei Besitzpruefungen ueber die Benutzerkennung bleiben unveraendert bestehen — die Bindung ERGAENZT sie, ersetzt sie nicht (die Regeln dieses Bereichs kennen keine Benutzerdimension)"
|
||
|
||
patterns-established:
|
||
- "Wachhund-Testfall, der VOR der Protokollpruefung beweist, dass der zu schuetzende Codepfad ueberhaupt durchlaufen wurde"
|
||
|
||
requirements-completed: [WINDOWS-18, ETAPPE-2-DASHBOARD]
|
||
|
||
coverage:
|
||
- id: D1
|
||
description: "13 Datenbankzugriffe des Bereichs dashboard vollstaendig entschieden: 12 gebunden (forTenant), 1 begruendet ungebunden (Modulkatalog)"
|
||
requirement: "ETAPPE-2-DASHBOARD"
|
||
verification:
|
||
- kind: unit
|
||
ref: "apps/api/src/dashboard/dashboard.service.spec.ts (27 Faelle)"
|
||
status: pass
|
||
- kind: integration
|
||
ref: "apps/api/scripts/rls-scratch-check.mjs (87/87 Pruefungen, davon 13 neue im Abschnitt runDashboardAreaChecks)"
|
||
status: pass
|
||
human_judgment: false
|
||
- id: D2
|
||
description: "Kritikschrift fuer den Bereich dashboard inklusive der beweisvernichtenden Schleife und der eigenstaendig nachgepruften widerlegten SearchProvider-Praemisse"
|
||
requirement: "WINDOWS-18"
|
||
verification:
|
||
- kind: other
|
||
ref: "docs/mandantentrennung-etappe2-fehlerrichtung.md, Abschnitt '## Bereich dashboard' (w1)-(w5)"
|
||
status: pass
|
||
human_judgment: false
|
||
- id: D3
|
||
description: "Klassifikationsdatei an allen fuenf handgepflegten Stellen nachgezogen und maschinell gegen den Quelltext geprueft"
|
||
verification:
|
||
- kind: unit
|
||
ref: "apps/api/src/prisma/rls-access-inventory.spec.ts (10/10)"
|
||
status: pass
|
||
human_judgment: false
|
||
|
||
duration: 26min
|
||
completed: 2026-09-11
|
||
status: 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`) in `apps/api/scripts/rls-scratch-check.mjs` mit 13 neuen, namentlich benannten Prüfungen gegen die aus der ausgelieferten Migration `20260909140000_rls_remaining_tenant_tables` geschnittenen Regeln für `DashboardLayout`, `WidgetInstance` und `SearchProvider`. Alle 87 Prüfungen bestehen (74 bisherige + 13 neue).
|
||
- Die Konfliktmessung (Befund K) ist gemessen, nicht angenommen: ein gebundenes `INSERT ... ON CONFLICT` auf 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()` wirft `PrismaClientUnknownRequestError`, NICHT die bekannte `P2002`-Form, die der Bereich `tenders` abfängt.
|
||
- `dashboard.service.ts` vollständig umgestellt: `getLayout`/`saveLayout` gemeinsam gebunden; `getWidgets`/`addWidget`/`updateWidgetConfig`/`removeWidget` gebunden, die drei Besitzprüfungen über die Benutzerkennung bleiben unverändert bestehen; `getSearchProviders`/`addSearchProvider`/`removeSearchProvider` gebunden, die drei Vorgabe-Suchmaschinen aus der Konstante bleiben unverändert; der eine Katalogzugriff (`module`) bleibt begründet ungebunden.
|
||
- `dashboard.controller.ts` reicht 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 aus `extractContext`/dem Sitzungsnachweis.
|
||
- `dashboard.service.spec.ts`: Zwei-Klienten-Nachweis über `__makeBoundClient` nach dem Muster von `module-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 dashboard` mit (w1)-(w5), inklusive der beweisvernichtenden Schleife (leeres Dashboard → Neuaufbau → automatisches Zurückschreiben → überschriebene Anordnung, Widget-Dubletten) und einem Nachtrag im Abschnitt `## Bereich module-registry` zur geerbten Bindungsentlastung.
|
||
- `docs/mandantentrennung-zugriffsklassifikation.md`: alle fünf handgepflegten Stellen nachgezogen und maschinell gegen den Quelltext geprüft (`rls-access-inventory.spec.ts` grü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:
|
||
|
||
1. **Aufgabe 1: Die Fehlerrichtung messen und aufschreiben** - `6744918` (feat)
|
||
2. **Aufgabe 2: Anordnung und Widgets binden** - `e0ce594` (feat, TDD)
|
||
3. **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 Abschnitt `runDashboardAreaChecks`, 13 neue Prüfungen (74 → 87 gesamt)
|
||
- `docs/mandantentrennung-etappe2-fehlerrichtung.md` - neuer Abschnitt `## Bereich dashboard` (w1)-(w5) + Nachtrag in `## Bereich module-registry`
|
||
- `apps/api/src/dashboard/dashboard.service.ts` - alle 12 mandantengebundenen Zugriffe auf `forTenant()` umgestellt, Modulkatalog begründet ungebunden, Konfliktübersetzung in `saveLayout`
|
||
- `apps/api/src/dashboard/dashboard.service.spec.ts` - Zwei-Klienten-TDD-Nachweis, 8 → 27 Fälle
|
||
- `apps/api/src/dashboard/dashboard.controller.ts` - fünf Handler reichen den Mandanten durch
|
||
- `docs/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 Bereich `tenders` abfängt. Das `tenders`-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-zeilenschutz` aus dem Bereich `module-registry` statt 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 `widgetInstance` sieben 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 mit `dashboardLayout` (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=8` gemessen 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.ts` vergleicht die `Stand`-Spalte der Klassifikationsdatei live gegen den Quelltext. Nach der Bindung von `dashboardLayout`/`widgetInstance` in Aufgabe 2 stand die Datei noch auf `ungebunden` (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`) auf `gebunden` gesetzt, 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 test` grü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=...`) nennt `docs/mandantentrennung-zugriffsklassifikation.md` nicht — 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')`) rief `getWidgets` mit leerer `mockMap` auf — der Katalogzugriff wurde dadurch nie erreicht (früher Rückgabepunkt bei `boundSlugs.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"):
|
||
|
||
1. **Aufgabe 2 — Bindung probeweise zurückgebaut.** `tenantPrisma.widgetInstance.delete` in `removeWidget` auf `this.prisma.widgetInstance.delete` zurü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).
|
||
|
||
2. **Aufgabe 3 — Katalogzugriff probeweise gebunden.** `this.prisma.module.findMany` in `getWidgets` auf `tenantPrisma.module.findMany` geändert (`module` ist bewusst nicht Teil der Testdouble-Bindungsliste `BOUND_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')` an `dashboard.service.ts:175`. Rückbau zurückgenommen, Testlauf wieder grün (27/27).
|
||
|
||
3. **Aufgabe 3 — Bestandsaufnahme-Zeile probeweise falsch gesetzt.** `Stand` der Zeile `dashboard.service.ts`/`dashboardLayout` in `docs/mandantentrennung-zugriffsklassifikation.md` auf `ungebunden` gesetzt (Quelltext ist tatsächlich `gebunden`). 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 `dashboard` ist 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 vorhandene `DashboardLayout`-Zeile für einen bekannten Benutzer, deren gebundener Lesezugriff `null` liefert.
|
||
- 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) oder `calendar`/`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 5b
|
||
`dashboardlayout-gebundenes-upsert-auf-unsichtbare-zeile-wirft-unknown`,
|
||
ueber `bound.dashboardLayout.upsert(...)` auf dem gebundenen GENERIERTEN
|
||
Client; geprueft wird der Konstruktorname des geworfenen Fehlers. Werkzeug
|
||
jetzt 88/88.
|
||
- Dabei aufgefallen: die Wegwerf-Tabelle `DashboardLayout` hatte kein
|
||
`createdAt`/`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 den `catch`-Zweig — die
|
||
Uebersetzung von `PrismaClientUnknownRequestError` in `ConflictException`,
|
||
und dass ein `PrismaClientKnownRequestError` (P2002) unveraendert
|
||
durchgereicht wird, weil er hier NICHT der gemessene Fall ist. Falsifiziert:
|
||
mit `if (false && ...)` im `catch` wird genau der erste Test rot, 28 bleiben
|
||
gruen; wiederhergestellt, `git diff` leer.
|
||
|
||
Endstand nach Nachtrag: 860 Tests gruen, Typpruefung sauber, 88/88.
|
||
|