Commit Graph

7 Commits

Author SHA1 Message Date
schalli 3f5afb0f54 feat(quick-260916-bwo): Dashboard-Raster verdoppelt (24 Spalten, 20 px, 8 px Abstand), Konstanten x2, einmalige Umrechnung gespeicherter Anordnungen mit Marker __gridVersion
- dashboard-grid.tsx: COLS 24/20/12/8/2, rowHeight 20, margin 8 (containerPadding folgt), Rueckfallwerte 4
- widget-registry.tsx: alle 32 Werte in WIDGET_CONSTRAINTS verdoppelt
- grid-layout-migration.ts (neu): migrateGridLayouts/withGridVersion, Marker nur im JSON, Idempotenz (T-BWO-02)
- dashboard-store.ts: Umrechnung beim Laden, Sofort-Speichern mit Marker, withGridVersion bei jedem saveLayout
- Tests: Migration 7 (neu), Store 6 (neu), Registry +1 (Tabelle), Grid +2 (Props ueber Mock), API-Spec +2 (Durchreichung __gridVersion, timeFontSizePt)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
2026-09-16 09:09:00 +02:00
schalli 07fc653f52 feat(quick-260911-nke): Benutzer an 34 Aufrufstellen gesetzt, zehn Tabellen gemessen, sechs Pruefungen umgedreht
- 30 verbleibende forTenant()-Aufrufstellen in sieben Diensten (calendar 6,
  dashboard 9, favorites 5, tender-email-config 3, tender-notification-pref 2,
  tender-rss-feed 2, tender-triage 3) reichen userId als drittes Argument
  durch. tender-digest.scheduler.ts bleibt zweistellig (Hintergrunddienst,
  Etappe 3c), mit Begruendung im Kommentar. Keine Methodensignatur, kein
  Controller angefasst, keine anwendungsseitige userId-Filterung entfernt.
- rls-scratch-check.mjs: zwoelf Extraktionsstellen auf die neue Migration
  umgeleitet (TenderEmailConfig/TenderNotificationPref/TenderSavedSearch/
  TenderTriage/TenderRssFeedSource in runTendersAreaChecks, SearchProvider in
  runSearchProviderAreaChecks/runDashboardAreaChecks, DashboardLayout/
  WidgetInstance, CalendarSource/FavoriteLink samt regelstand-eindeutig-Gates).
  SearchProvider/TenderRssFeedSource jetzt mit extractAllPolicySql (4 Regeln).
  runUserDimensionChecks() um die uebrigen neun Tabellen erweitert (neue
  Routine runCommandSeparatedPersonalTableCheck fuer die zwei NULL-faehigen
  Tabellen inkl. gemeinsame-Zeile-Pruefungen).
- Sechs Loch-Pruefungen umgedreht (dashboardlayout, widgetinstance,
  searchprovider, calendarsource, favoritelink-Doppelaussage getrennt) —
  alte Messung ohne Benutzer bleibt unter neuem Namen, Umkehrung MIT
  Benutzer erwartet das Gegenteil; kein alter Name mehr als Kennung.
- Baseline: 1020/62 Tests weiterhin gruen, Typpruefung sauber, Werkzeug
  203/203 bestanden (vorher 146).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-11 17:39:17 +02:00
schalli 6e7120648b fix(quick-260910-krx): Konfliktklasse ueber den generierten Client messen statt sie zu behaupten 2026-09-11 09:16:39 +02:00
schalli 67b50240d6 feat(quick-260910-krx): Suchmaschinen gebunden, Katalog begruendet offen, Klassifikation nachgezogen
Aufgabe 3 — TDD zuerst (7 weitere Faelle in dashboard.service.spec.ts, 20
vorher/27 nach dieser Aufgabe), dann die Umstellung:

- getSearchProviders/addSearchProvider/removeSearchProvider laufen ueber
  forTenant(); removeSearchProvider fuehrt Besitzpruefung UND Schreibzugriff
  ueber DENSELBEN gebundenen Klienten. Die drei Vorgabe-Suchmaschinen aus
  der Konstante bleiben unveraendert vorangestellt.
- Der eine Katalogzugriff (this.prisma.module in getWidgets) bleibt
  begruendet ungebunden: Messung und Bedingung getrennt (Tabelle traegt
  heute keinen Zeilenschutz, wirkungslos statt katastrophal — katastrophal
  erst, wenn Etappe 3 eine Regel gibt), unter Berufung auf die bestehende
  Werkzeugpruefung module-tabelle-traegt-keinen-zeilenschutz statt einer
  neuen Behauptung. Ein Wachhund-Testfall haelt den Katalogzugriff aus dem
  Bindungsprotokoll heraus (und beweist zuerst, dass der Katalogpfad
  tatsaechlich durchlaufen wird, nicht nur theoretisch geprueft ist).
- docs/mandantentrennung-zugriffsklassifikation.md an allen fuenf
  handgepflegten Stellen nachgezogen: vier Bestandsaufnahme-Zeilen (inkl.
  eigenstaendiger Nachpruefung der widerlegten SearchProvider-Praemisse),
  Uebersichtszeile (13/0 -> 1/12), Summenzeile (95/147), Klassen-Verteilung
  (unveraendert 63 Paare, ausdruecklich vermerkt), Hintergrunddienst-
  Abschnitt (dashboard hat keinen sechsten Fall, mit Messanweisung), "Was
  diese Etappe NICHT entscheidet" (dienst-interner forTenant()-Weg wie alle
  sieben Bereiche vor ihm).
- .planning/WINDOWS.md traegt Eintrag #25 (offen, Tabelle + JSON): die
  beweisvernichtende Schleife (leeres Dashboard -> Neuaufbau ->
  automatisches Zurueckschreiben -> ueberschriebene Anordnung, Widget-
  Dubletten) samt der Vorabpruefung fuer Etappe 4 und dem Verweis auf #22
  fuer die verwandte Eindeutigkeitsfrage.

Zwei weitere Falsifizierungsnachweise durchgefuehrt: (1) den Katalogzugriff
probeweise gebunden (tenantPrisma.module.findMany) — acht Tests werden rot
mit "TypeError: Cannot read properties of undefined (reading 'findMany')",
weil `module` bewusst nicht in der Testdouble-Bindungsliste steht; Rueckbau
zurueckgenommen, 27/27 wieder gruen. (2) den Stand von dashboardLayout in
der Klassifikationsdatei probeweise auf "ungebunden" gesetzt —
rls-access-inventory.spec.ts wird rot mit "Abweichender Stand (Dokument vs.
Quelltext): ... dokumentiert=ungebunden, gemessen=gebunden"; Ruecknahme,
Testlauf wieder gruen (10/10).

Baseline gehalten: 858 Tests / 56 Dateien gruen, Typpruefung sauber,
Wegwerf-Werkzeug 87/87. Schalter bleibt aus.
2026-09-11 09:03:23 +02:00
schalli e0ce594c5a feat(quick-260910-krx): Anordnung und Widgets an forTenant() gebunden
Aufgabe 2 — TDD zuerst (20 Faelle in dashboard.service.spec.ts, 8 vorher/12
neu, Zwei-Klienten-Nachweis ueber __makeBoundClient nach dem Muster von
module-access.service.spec.ts), dann die Umstellung:

- getLayout/saveLayout laufen GEMEINSAM gebunden (ein Testfall nagelt das
  fest); saveLayout uebersetzt eine gebundene Konflikt-Schreibung
  (PrismaClientUnknownRequestError, gemessen in Aufgabe 1 — NICHT P2002)
  in eine deutsche ConflictException.
- getWidgets/addWidget/updateWidgetConfig/removeWidget laufen gebunden;
  die drei Besitzpruefungen ueber die Benutzerkennung bleiben unveraendert
  bestehen (die Regeln dieses Bereichs kennen keine Benutzerdimension).
  updateWidgetConfig/removeWidget fuehren Besitzpruefung UND Schreibzugriff
  ueber DENSELBEN gebundenen Klienten.
- dashboard.controller.ts reicht den bereits aufgeloesten Mandanten bei
  getLayout/updateWidgetConfig/removeWidget durch (keine neue
  Vertrauensquelle, weiterhin aus extractContext/Sitzungsnachweis).
- Der Modulkatalog und die vier Suchmaschinenzugriffe bleiben in dieser
  Aufgabe unveraendert (Aufgabe 3).
- Falsifizierungsnachweis durchgefuehrt: tenantPrisma.widgetInstance.delete
  probeweise auf this.prisma zurueckgebaut — Test "Widget entfernen: ebenso,
  beide Abfragen ueber denselben Klienten" wird rot mit "erwarteter
  gebundener Aufruf widgetInstance.delete(tenant=tenant-1) fehlt im
  Protokoll"; Rueckbau zurueckgenommen, Testlauf wieder gruen (20/20).

Zwei dokumentierte Abweichungen (Rule 3): (1) Befund A hatte fuer
widgetInstance sieben Treffer vorhergesagt, gemessen sind sechs (macht
zusammen mit dashboardLayout acht statt neun) — der Verify-Schwellwert
wird entsprechend auf >=8 gelesen. (2) Die Stand-Spalte fuer
dashboardLayout/widgetInstance in der Klassifikationsdatei wird bereits
hier minimal nachgezogen (nicht erst in Aufgabe 3), weil
rls-access-inventory.spec.ts sonst am Ende dieser Aufgabe rot waere —
derselbe Praezedenzfall wie 260910-exd, Aufgabe 2.

Baseline gehalten: 851 Tests / 56 Dateien gruen (839 + 12 neue), Typpruefung
sauber, Wegwerf-Werkzeug 87/87.
2026-09-11 08:54:22 +02:00
schalli 0ff46acd75 feat(15-05): getWidgets filters via ModuleAccessService (D-22, PERM-07)
- DashboardModule imports ModuleRegistryModule to inject ModuleAccessService
- getWidgets(userId, tenantId, role) runs the existing findMany unchanged
  first, then calls getAccessibleModuleIds exactly once — only if a loaded
  widget's type is in WIDGET_MODULE_MAP (currently always empty, so no
  lookup runs today); unresolved module slugs fail closed
- DashboardController.getWidgets forwards tenantId + role from the JWT
- dashboard.service.spec.ts (8 tests, TDD-GREEN): covers every <behavior>
  case incl. D-03 ADMIN bypass, adjacency/empty/ordering/idempotency, and
  fail-closed on an unresolved Module slug
- pnpm --filter @tessera/api test: 457/457 green; type-check clean
- manual e2e against local API + DB container: empty WIDGET_MODULE_MAP
  leaves an existing user's widget count unchanged (2/2 clock+search
  survived the filter), throwaway verification user/rows removed after
2026-08-04 15:37:28 +02:00
schalli d0ff6f0bc0 test(15-05): add failing test for module-filtered getWidgets
- covers every <behavior> case from 15-05-PLAN.md task 2, including the
  adjacency/empty/ordering/idempotency edge-probe categories and the
  fail-closed unresolved-slug case
- RED confirmed: 4/8 fail against the current 1-arg getWidgets(userId)
2026-08-04 15:31:35 +02:00