docs(quick-260911-cwh): complete Bereich calendar plan

This commit is contained in:
2026-09-11 10:01:30 +02:00
parent e0e163ec63
commit 06038b9dfa
2 changed files with 175 additions and 5 deletions
@@ -0,0 +1,168 @@
---
phase: quick-260911-cwh
plan: 01
subsystem: database
tags: [prisma, postgresql, row-level-security, multi-tenancy, nestjs, vitest, calendar]
requires:
- phase: quick-260910-krx
provides: Bereich dashboard vollstaendig gebunden, Muster fuer generierten-Client-Messung (Pruefung 5b)
provides:
- "Bereich calendar (calendar.service.ts, Modell calendarSource) vollstaendig an forTenant() gebunden, alle zwoelf Datenbankzugriffe"
- "Elfter Abschnitt runCalendarAreaChecks in rls-scratch-check.mjs mit 13 neuen Pruefungen (davon vier ueber den generierten Client)"
- "calendar.service.spec.ts NEU angelegt — erste Testlage dieses Bereichs, 23 Testfaelle mit Zwei-Klienten-Nachweis"
- "Kritikschrift Abschnitt '## Bereich calendar' mit gemessenem Cache-Schluessel-Urteil und Zugangsdaten-Erhaltungsbefund"
- "WINDOWS #26: offener Ledger-Eintrag zur lautlosen Fehlerrichtung im Bereich calendar"
affects: [etappe-2-mandantentrennung, calendar-modul, rls-scratch-check-tooling]
actuals:
tokens: 25378
tasks: 3
commits: 3
plan_head_before: 508d9e43015df192f5b86c39ac1e81c983f100b8
tech-stack:
added: []
patterns:
- "forTenant(this.prisma, tenantId) als lokale tenantPrisma-Konstante je Methode mit Datenbankzugriff (dienst-interner Weg, wie alle acht Bereiche vor calendar)"
- "Zwei-Klienten-Testnachweis via __makeBoundClient(tenantId) — ungebundener Fake protokolliert nicht, gebundener schon"
- "Generierter-Client-Messung an einer schemagleichen Wegwerf-Tabelle (Spaltenmenge zur Laufzeit gegen schema.prisma geprueft) statt Roh-SQL, fuer Fehlerklassen die die Anwendung tatsaechlich sieht"
key-files:
created:
- apps/api/src/calendar/calendar.service.spec.ts
modified:
- apps/api/scripts/rls-scratch-check.mjs
- docs/mandantentrennung-etappe2-fehlerrichtung.md
- apps/api/src/calendar/calendar.service.ts
- apps/api/src/calendar/calendar.controller.ts
- docs/mandantentrennung-zugriffsklassifikation.md
- .planning/WINDOWS.md
key-decisions:
- "Cache-Schluessel (userId:from:to) bleibt OHNE Mandantenanteil — die vierteilige Kette (schema.prisma User.id @default(uuid()) -> JwtStrategy.validate -> auth.service.ts sub:user.id -> extractContext) zeigt eine plattformweit eindeutige UUID, die Etappe-3-Entscheidung (1) betrifft User.username/User.email, nicht User.id"
- "Keine neue Fehleruebersetzung fuer die drei Besitzpruefungen noetig: der gemessene Wettlauf-Fall wirft PrismaClientKnownRequestError/P2025 (nicht PrismaClientUnknownRequestError wie im Bereich dashboard), und dieser Pfad ist durch die vorgeschaltete findUnique-Besitzpruefung strukturell unerreichbar"
- "refreshCacheInBackground wird NICHT als sechster Hintergrunddienst-Fall gefuehrt — sie iteriert nicht ueber Mandanten, sondern traegt den Mandanten der Anfrage, die sie angestossen hat (Befund B)"
- "Antwortsemantik 403 (Forbidden bei fremdem Besitz) vs. 404 (NotFound bei unbekannter Kennung) bleibt unveraendert — waere eine API-Aenderung ausserhalb des Auftrags"
requirements-completed: [WINDOWS-18, ETAPPE-2-CALENDAR]
coverage:
- id: D1
description: "Alle zwoelf Datenbankzugriffe von calendar.service.ts laufen gebunden ueber forTenant(), ein tenantPrisma-Klient je Methode mit Datenbankzugriff"
requirement: ETAPPE-2-CALENDAR
verification:
- kind: unit
ref: "apps/api/src/calendar/calendar.service.spec.ts — 23 Testfaelle"
status: pass
- kind: unit
ref: "apps/api/src/prisma/rls-access-inventory.spec.ts — Bestandsaufnahme stimmt mit Quelltext ueberein"
status: pass
- kind: other
ref: "node apps/api/scripts/rls-scratch-check.mjs — runCalendarAreaChecks, 13 neue Pruefungen"
status: pass
human_judgment: false
- id: D2
description: "Kritikschrift misst die umgekehrte Fehlerrichtung an der ausgelieferten Regel (Migration 20260909140000, bestaetigt unveraendert durch 20260910120000) inklusive vier Pruefungen ueber den generierten Prisma-Client an einer schemagleichen Wegwerf-Tabelle"
requirement: WINDOWS-18
verification:
- kind: other
ref: "node apps/api/scripts/rls-scratch-check.mjs — Pruefungen calendarsource-wegwerftabelle-deckt-alle-spalten-des-generierten-clients, calendarsource-generierter-client-*"
status: pass
human_judgment: false
- id: D3
description: "Fuenf handgepflegte Dokumentstellen der Klassifikation nachgezogen und maschinell gegen den Quelltext gegatet; WINDOWS-Ledger-Eintrag #26 in Tabelle, JSON-Block und Kopfzaehlern konsistent"
verification:
- kind: unit
ref: "apps/api/src/prisma/rls-access-inventory.spec.ts"
status: pass
- kind: other
ref: "Plan-Verify-Gates Aufgabe 3 (Uebersichtszeile, Summenzeile, Klassen-Verteilung, Hintergrunddienst-Abschnitt, WINDOWS.md-Konsistenz) — manuell in der Ausfuehrung nachvollzogen"
status: pass
human_judgment: false
duration: 21min
completed: 2026-09-11
status: complete
---
# Quick Task 260911-cwh: Etappe 2 Mandantentrennung, Bereich calendar — Summary
**Alle zwoelf `calendarSource`-Datenbankzugriffe in `calendar.service.ts` auf `forTenant()` umgestellt (ein `tenantPrisma`-Klient je Methode), mit einer aus dem Nichts angelegten Testlage (23 Faelle), 13 neuen Wegwerf-Pruefungen — vier davon ueber den generierten Prisma-Client — und einem gemessenen, schriftlich festgehaltenen Urteil zum Cache-Schluessel sowie zur Zugangsdaten-Erhaltung.**
## Performance
- **Duration:** 21 min
- **Started:** 2026-09-11T07:39:07Z
- **Completed:** 2026-09-11T08:00:00Z (ca.)
- **Tasks:** 3
- **Files modified:** 7 (6 modified, 1 neu angelegt)
## Accomplishments
- Neunter Bereich der Etappe 2 (Mandantentrennung) abgeschlossen: `calendar` — der einzige bisher umgestellte Bereich, der verschluesselte Zugangsdaten zu FREMDEN Servern (Exchange/CalDAV/ICS) haelt
- `apps/api/scripts/rls-scratch-check.mjs`: elfter Abschnitt `runCalendarAreaChecks` mit 13 namentlich benannten Pruefungen, davon vier ueber den generierten Client an einer Wegwerf-Tabelle, deren 17-Spalten-Deckung zur Laufzeit gegen `schema.prisma` geprueft wird — alle 101 Pruefungen des Werkzeugs bestehen
- `calendar.service.spec.ts`: NEU angelegt (dieser Bereich hatte zuvor KEINE Testdatei), 23 Testfaelle mit Zwei-Klienten-Nachweis, decken jeden Datenbankpfad, alle drei Besitzpruefungen je Ausnahmeart, die drei Zugangsdaten-Erhaltungsfaelle, beide Rueckschreibungen der Aggregationsschleife und den Wachhund fuer "genau ein Klient je Aufruf" ab
- Kritikschrift (`docs/mandantentrennung-etappe2-fehlerrichtung.md`, Abschnitt `## Bereich calendar`) mit (k1)-(k5): die tatsaechlich gemessene Fehlerklasse des Wettlauf-Falls (`PrismaClientKnownRequestError`/P2025, ANDERS als im Bereich `dashboard`), das vierteilige Cache-Schluessel-Urteil, die Backend- UND Frontend-Leere-als-Abwesenheit-Kette samt Fehlerverschluckung
- Fuenf handgepflegte Stellen der Klassifikation nachgezogen (Bestandsaufnahme-Zeile, Uebersichtszeile 0/12, Summenzeile 83/159, Klassen-Verteilung mit Stand-Vermerk, Hintergrunddienst-Abschnitt mit dem Sonderfall `refreshCacheInBackground`), WINDOWS-Ledger-Eintrag #26 angelegt
## Task Commits
Each task was committed atomically:
1. **Aufgabe 1: Fehlerrichtung messen** - `bf5fc4d` (feat)
2. **Aufgabe 2: Testlage anlegen, alle zwoelf Zugriffe binden** - `77cb124` (feat)
3. **Aufgabe 3: Restliche Dokumentstellen, WINDOWS-Ledger, Falsifizierungsnachweise** - `e0e163e` (docs)
_Kein TDD-Modus fuer diesen Plan — die Testlage wurde in Aufgabe 2 als Voraussetzung neu angelegt, nicht als RED/GREEN-Zyklus einzeln entwickelt._
## Files Created/Modified
- `apps/api/scripts/rls-scratch-check.mjs` - elfter Abschnitt `runCalendarAreaChecks` (13 neue Pruefungen), Helfer `readSchemaModelFieldNames()`
- `docs/mandantentrennung-etappe2-fehlerrichtung.md` - Abschnitt `## Bereich calendar` mit (k1)-(k5)
- `apps/api/src/calendar/calendar.service.spec.ts` - NEU, 23 Testfaelle, Zwei-Klienten-Nachweis
- `apps/api/src/calendar/calendar.service.ts` - alle zwoelf Zugriffe gebunden, Klassenkommentar praezisiert, Cache-Schluessel-Urteil und Hintergrunddienst-Begruendung als Kommentare
- `apps/api/src/calendar/calendar.controller.ts` - sechs kontextnutzende Handler reichen Benutzer- UND Mandantenkennung durch
- `docs/mandantentrennung-zugriffsklassifikation.md` - fuenf handgepflegte Stellen nachgezogen
- `.planning/WINDOWS.md` - neuer offener Eintrag #26
## Decisions Made
- Cache-Schluessel bleibt ohne Mandantenanteil — vierteilige Kette gemessen, Urteil in Code UND Kritikschrift verankert (siehe `key-decisions` oben)
- Keine neue Fehleruebersetzung fuer die Besitzpruefungen: gemessener Wettlauf-Fall (P2025) ist ueber die vorgeschaltete Pruefung strukturell unerreichbar
- `refreshCacheInBackground` bleibt ausserhalb des Hintergrunddienst-Abschnitts (kein sechster Fall) — sie traegt den Mandanten der Anfrage, iteriert nicht ueber mehrere Mandanten
- Antwortsemantik 403/404 bewusst unveraendert gelassen
## Deviations from Plan
None - plan executed exactly as written. Alle Planungsbefunde (A-L) wurden zur Ausfuehrungszeit nachgeprueft und bestaetigt; keine Abweichung von den Planungsbefunden trat auf.
## Falsifizierungsnachweise (drei, alle durchgefuehrt und zurueckgenommen)
1. **Aufgabe 2 — halb gebundene Aggregationsschleife:** Die Bindung der Synchronstatus-Rueckschreibung im Erfolgspfad von `fetchAndCacheEvents` wurde probeweise zurueckgenommen (`tenantPrisma.calendarSource.update` → `this.prisma.calendarSource.update`). Ergebnis: der benannte Test `aggregateEvents, Erfolgspfad: das Laden der Quellen UND die Synchronstatus-Rueckschreibung bei Erfolg stehen gebunden unter derselben Mandantenkennung im Protokoll` ging rot mit der Meldung `erwarteter gebundener Aufruf calendarSource.update(tenant=t1) fehlt im Protokoll: [{"tenantId":"t1","model":"calendarSource","method":"findMany"}]: expected false to be true`. Bindung wiederhergestellt, Testlage danach wieder gruen (23/23).
2. **Aufgabe 3a — falscher Stand in der Bestandsaufnahme:** Die Zeile `calendar.service.ts`/`calendarSource` wurde probeweise auf `Stand: ungebunden` zurueckgesetzt. Ergebnis: `rls-access-inventory.spec.ts` ging rot mit `Abweichender Stand (Dokument vs. Quelltext): apps/api/src/calendar/calendar.service.ts::calendarSource — dokumentiert=ungebunden, gemessen=gebunden`. Zurueckgesetzt, Test danach wieder gruen (10/10).
3. **Aufgabe 3b — falsche Uebersichtszahl:** Die Uebersichtszeile `calendar` wurde probeweise auf `5 | 12` (statt der gemessenen `0 | 12`) gesetzt. Ergebnis: das herleitende Gate (`grep -qE "^\| calendar \| ${DU} \| ${DB} \| \*\*war 12/0\*\*"`) meldete `UEBERSICHTSZEILE calendar nennt nicht die neu gemessenen Zahlen 0/12 im etablierten Stil`. Zurueckgesetzt, Gate danach wieder erfuellt.
## Issues Encountered
None.
## User Setup Required
None - keine externe Diensteinrichtung erforderlich.
## Next Phase Readiness
- Neunter von zwoelf Bereichen der Etappe 2 abgeschlossen — Klassen-Verteilung (63 Paare) unveraendert, nur die `Stand`-Spalte des `calendar`-Paares gewechselt
- Der Schalter (`DATABASE_URL` → Rolle `tessera`, `BYPASSRLS`) bleibt AUS — keine Compose-/Umgebungsdatei angefasst, keine Schema-/Migrationsaenderung, nichts in Active Directory
- Baseline am Ende gehalten: 883 Tests gruen (57 Dateien, davon 23 neu), Typpruefung sauber, `rls-scratch-check.mjs` meldet 101/101 Pruefungen bestanden
- WINDOWS #26 bleibt bis nach dem Scharfschalten (Etappe 4) offen — an dieselbe Bedingung gebunden wie #18, Familie mit #23/#25
- Naechster Bereich der Etappe 2 (zehnter von zwoelf) ist aus der Klassen-Verteilung in `docs/mandantentrennung-zugriffsklassifikation.md` zu waehlen
## Self-Check: PASSED
All created/modified files verified present on disk; all three task commit hashes (`bf5fc4d`, `77cb124`, `e0e163e`) verified present in git history.
---
*Phase: quick-260911-cwh*
*Completed: 2026-09-11*