wiki/z-archive/concepts/Multi-Mandate-Umsetzungsplan.md

915 lines
35 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Umsetzungsplan: Multi-Mandate Onboarding, Store und Unified Data Layer
**Grundlage:** [Multi-Mandate-Onboarding-und-Store-Konzept.md](./Multi-Mandate-Onboarding-und-Store-Konzept.md) v4
**Struktur:** Der Plan folgt den drei Phasen aus dem Konzept. Jede Phase ist in konkrete Arbeitspakete (AP) zerlegt. Jedes AP enthält: betroffene Dateien, Abhängigkeiten und Akzeptanzkriterien.
**Legende:** `[BE]` = Backend, `[FE]` = Frontend, `[DB]` = Datenbank/Migration, `[OPS]` = Betrieb/Infrastruktur
---
## Phase 1 — Mandate-per-Registration und Own-Instance-Pattern
> Ziel: Saubere Mandanten-Grenze, eigene Instanzen pro Mandant, Root nur noch technisch. `FeatureInstance` = Datengrenze.
---
### AP 1.1 — `[DB]` Datenmodell: `mandateType` Enum auf `Mandate`
**Beschreibung:** Neues Feld `mandateType` auf dem `Mandate`-Modell. Enum mit Werten `system`, `personal`, `company`. Mutabel und rein informativ — keine Geschäftslogik daran gebunden.
**Dateien:**
- `gateway/modules/datamodels/datamodelUam.py` — Enum `MandateType` hinzufügen, Feld `mandateType` auf `Mandate`
**Schritte:**
1. Enum `MandateType` definieren (`system`, `personal`, `company`)
2. Feld `mandateType: MandateType` auf `Mandate` mit Default `company`
3. Bestehende Mandanten erhalten via DB-Update Default `company`
4. Root-Mandant explizit auf `system` setzen (im Bootstrap)
**Akzeptanz:**
- Alle bestehenden Mandanten haben `mandateType=company`
- Root-Mandant hat `mandateType=system`
- Keine Geschäftslogik prüft `mandateType` für Feature-Gates oder Kapazität
**Abhängigkeiten:** Keine
---
### AP 1.2 — `[BE]` Subscription-Timing: `PENDING` → `ACTIVE`
**Beschreibung:** `MandateSubscription` wird bei Registrierung mit Status `PENDING` erstellt. Aktivierung auf `ACTIVE` erfolgt bei erstem Login. Trial-Periode beginnt erst ab Aktivierung.
**Dateien:**
- `gateway/modules/datamodels/datamodelSubscription.py` — Status `PENDING` in die State Machine aufnehmen
- `gateway/modules/routes/routeSecurityLocal.py` — Subscription mit `PENDING` erstellen
- `gateway/modules/routes/routeSecurityGoogle.py` — Subscription mit `PENDING` erstellen
- Login-Logik (Local + OAuth) — bei erstem Login `PENDING``ACTIVE` Transition auslösen
**Schritte:**
1. `PENDING` als neuen Status in `SubscriptionStatus` Enum hinzufügen
2. State-Machine-Transition `PENDING → ACTIVE` definieren
3. Bei Registrierung: `MandateSubscription` mit `status=PENDING`, `trialStartDate=null`
4. Bei erstem Login: Status auf `ACTIVE` setzen, `trialStartDate=now()`
5. Bestehende Subscriptions bleiben unverändert (`ACTIVE`)
**Akzeptanz:**
- Neue Registrierungen erhalten `PENDING`-Subscription
- Erster Login setzt auf `ACTIVE` und startet Trial
- Kein User verliert Trial-Tage durch Magic-Link-Delay
**Abhängigkeiten:** AP 1.1
---
### AP 1.3 — `[BE]` Interne Provisionierungsroutine `_provisionMandateForUser`
**Beschreibung:** Zentrale atomare Funktion, die nach `createUser` ein komplettes Mandate mit Subscription und Feature-Instanzen erstellt. Umgeht RBAC (Root-privilegiert), da der User noch keine Rechte hat.
**Dateien:**
- `gateway/modules/interfaces/interfaceDbApp.py` — neue Funktion `_provisionMandateForUser()`
**Schritte:**
1. Funktion `_provisionMandateForUser(userId, mandateType, mandateName, planKey)` erstellen
2. Atomare Transaktion:
a. `createMandate` (mit `mandateType`, ohne RBAC-Check)
b. System-Rollen kopieren (`_copySystemRolesToMandate`)
c. `createUserMandate` mit Admin-Rolle
d. `MandateSubscription` erstellen (mit `PENDING`)
e. Für Features mit `autoCreateInstance`: `createFeatureInstance` + `FeatureAccess` mit Admin-Instanzrolle
3. Rollback bei Fehler in einem Schritt
4. Logging aller Aktionen
**Akzeptanz:**
- Ein Aufruf erstellt alles atomar (Mandate + UserMandate + Subscription + Instanzen)
- Bei Fehler: kein Halbzustand (keine verwaisten Mandate)
- RBAC wird nicht geprüft (interne Routine)
**Abhängigkeiten:** AP 1.1, AP 1.2
---
### AP 1.4 — `[BE]` Registrierung Local: Mandate-Provisionierung
**Beschreibung:** `POST /api/local/register` erweitern um `registrationType` und `companyName`. Nach `createUser` wird `_provisionMandateForUser` aufgerufen.
**Dateien:**
- `gateway/modules/routes/routeSecurityLocal.py` — Register-Endpoint erweitern
**Schritte:**
1. Request-Body erweitern: `registrationType` (personal/company), `companyName` (optional)
2. Validierung: `company` erfordert `companyName`
3. Nach `createUser`: `_provisionMandateForUser` aufrufen mit:
- `personal``mandateType=personal`, `planKey=TRIAL_7D`, `mandateName=username`
- `company``mandateType=company`, `planKey=STANDARD_MONTHLY`, `mandateName=companyName`
4. Response erweitern um `mandateId`
**Akzeptanz:**
- Registrierung erstellt User + Mandate + Subscription + Instanzen in einem Flow
- Personal → Trial, Company → Standard
- Fehler bei Provisionierung → User wird nicht erstellt (oder cleanup)
**Abhängigkeiten:** AP 1.3
---
### AP 1.5 — `[BE]` Registrierung OAuth: Onboarding-Wizard
**Beschreibung:** Bei OAuth-Registrierung (Google, MSAL) gibt es kein Formular. Stattdessen wird beim allerersten Login erkannt, dass kein Mandate existiert, und ein Onboarding-Wizard-Flag gesetzt.
**Dateien:**
- `gateway/modules/routes/routeSecurityGoogle.py` — Erkennung „erster Login ohne Mandate"
- Analoge Logik für MSAL-Route
**Schritte:**
1. Nach OAuth-Login prüfen: Hat der User mindestens ein Mandate?
2. Falls nein: Response-Flag `requiresOnboarding=true` setzen
3. Frontend zeigt Onboarding-Wizard (siehe AP 1.12)
4. Wizard-Submit ruft neuen Endpoint auf: `POST /api/onboarding/provision` mit `mandateType` + `companyName`
5. Endpoint nutzt `_provisionMandateForUser`
**Akzeptanz:**
- OAuth-User ohne Mandate wird erkannt
- Wizard erscheint nur beim allerersten Login
- Nach Wizard-Abschluss hat der User ein vollständiges Mandate
**Abhängigkeiten:** AP 1.3
---
### AP 1.6 — `[BE]` Feature Store: Refactoring auf Own Instance Pattern
**Beschreibung:** Store erstellt eigene `FeatureInstance` im Ziel-Mandanten statt Shared Instance im Root. Explizites `mandateId` in allen Store-APIs. Mehrere Instanzen desselben Features sind erlaubt.
**Dateien:**
- `gateway/modules/routes/routeStore.py` — komplettes Refactoring
**Schritte:**
1. `POST /api/store/activate`:
- Body: `featureCode` + `mandateId` (explizit, nie implizit)
- Prüfungen: User ist Admin im Ziel-Mandat, Subscription-Kapazität (`maxFeatureInstances`)
- Neue `FeatureInstance` im Ziel-Mandat erstellen (auch wenn bereits eine gleiche existiert)
- `FeatureAccess` + Admin-Instanzrolle für den User
- Stripe-Quantity sync
2. `POST /api/store/deactivate`:
- Body: `featureCode` + `mandateId` + `instanceId`
- Eigenen `FeatureAccess` entfernen
- Orphan-Check: Keine `FeatureAccess`-Records mehr → Instanz löschen + Stripe-Quantity sync
3. `GET /api/store/features`:
- Zeigt verfügbare Features mit Status pro Mandat des Users
- Für jeden Mandanten des Users: Anzahl aktiver Instanzen
4. Alte Shared-Instance-Logik entfernen
**Akzeptanz:**
- Aktivierung erstellt immer eine neue Instanz im expliziten Mandat
- Mehrere Instanzen desselben Features pro Mandat möglich
- Orphan Control: letzte Deaktivierung löscht Instanz
- Kein `mandateId`-Guessing, kein implizites Primary-Mandate
**Abhängigkeiten:** AP 1.1, AP 1.3
---
### AP 1.7 — `[BE]` Store: Auto-Mandate für User ohne Admin-Mandate
**Beschreibung:** User, die nur als Mitglieder (nicht Admin) in fremden Mandanten sind, können über den Store ein eigenes Mandate erstellen lassen.
**Dateien:**
- `gateway/modules/routes/routeStore.py` — Activate-Endpoint erweitern
**Schritte:**
1. Bei `activate`: Prüfen ob User mindestens ein Mandate hat, in dem er Admin ist
2. Falls kein Admin-Mandate vorhanden und `mandateId` nicht angegeben:
- `_provisionMandateForUser` aufrufen (personal, TRIAL_7D)
- Feature-Instanz im neuen Mandate erstellen
- Response enthält neues `mandateId`
3. Falls `mandateId` angegeben aber User kein Admin dort: Fehler (403)
**Akzeptanz:**
- User ohne Admin-Mandate kann Feature im Store aktivieren → Auto-Mandate
- User wird automatisch Admin des neuen Mandanten
- Bestehende Admins nutzen weiterhin den normalen Flow
**Abhängigkeiten:** AP 1.6, AP 1.3
---
### AP 1.8 — `[BE]` Subscription: `maxDataVolumeMB` Parameter
**Beschreibung:** Neuer Parameter `maxDataVolumeMB` in der Subscription für Kapazitätsplanung (Soft-Limit).
**Dateien:**
- `gateway/modules/datamodels/datamodelSubscription.py``BUILTIN_PLANS` erweitern
- `gateway/modules/interfaces/interfaceDbApp.py` — Volumen-Berechnung
**Schritte:**
1. `maxDataVolumeMB` als Parameter in `SubscriptionPlan` hinzufügen
2. Default-Werte pro Plan definieren (z. B. TRIAL_7D: 500 MB, STANDARD: 10 GB)
3. API-Endpoint für aktuelle Nutzung: `GET /api/mandate/{id}/data-usage`
4. Berechnung: Summe aller Dateigrössen im Mandate
**Akzeptanz:**
- Subscription-Pläne enthalten `maxDataVolumeMB`
- Aktuelle Nutzung ist abrufbar
- Kein Schreib-Block bei Überschreitung (Soft-Limit)
**Abhängigkeiten:** AP 1.2
---
### AP 1.9 — `[DB]` Migrations-Script: Root-Mandant bereinigen
**Beschreibung:** Separates Script, das alle Endkunden-Daten aus dem Root-Mandate in eigene Mandate migriert. Einmalig im Bootstrap aufgerufen.
**Dateien:**
- `gateway/scripts/script_migrate_root_users.py` (oder `gateway/modules/migration/migrateRootUsers.py`) — NEU
- `gateway/modules/interfaces/interfaceBootstrap.py` — Aufruf einhängen
**Schritte:**
1. Script erstellen mit Dry-Run-Modus
2. **Schritt 1 — Feature-Daten migrieren:**
- Für jeden User mit FeatureAccess auf Root-Instanzen:
- Hat User eigenes Mandate? → Ziel-Mandate setzen
- Kein Mandate? → `_provisionMandateForUser` (personal)
- Pro FeatureAccess: Neue Instanz im Ziel-Mandat erstellen, Daten umschreiben (`featureInstanceId`), FeatureAccess transferieren
3. **Schritt 2 — Root bereinigen:**
- Alle Feature-Instanzen im Root entfernen
- UserMandate zum Root entfernen (ausser `isSysAdmin=true`)
4. Root-Mandate: `mandateType=system` setzen
5. Flag in DB setzen: Migration abgeschlossen (verhindert Doppelausführung)
6. Im Bootstrap einhängen nach `initRootMandateFeatures`
**Akzeptanz:**
- Dry-Run zeigt alle Aktionen ohne Änderungen
- Alle User haben eigene Mandate mit migrierten Daten
- Root enthält nur sysadmin-User, keine Feature-Instanzen
- Script läuft nur einmal (Flag-Check)
**Abhängigkeiten:** AP 1.1, AP 1.3
---
### AP 1.10 — `[BE]` Mandanten-Löschung: Kaskade
**Beschreibung:** Vollständige Lösch-Kaskade für Mandanten mit Soft-Delete und Schutzmechanismen.
**Dateien:**
- `gateway/modules/interfaces/interfaceDbApp.py` — neue Funktion `_deleteMandate()`
- `gateway/modules/shared/gdprDeletion.py` — erweitern
- API-Endpoint: `DELETE /api/mandates/{mandateId}`
**Schritte:**
1. Schutzmechanismen:
- `isSystem=true` → Löschung verweigern
- Nur Mandanten-Admin oder sysadmin darf löschen
- Bestätigung via `confirmMandateName` im Request-Body
2. Soft-Delete:
- `deletedAt` Timestamp auf Mandate setzen
- Alle Zugriffe sofort sperren (RBAC-Check auf `deletedAt`)
- 30-Tage Retention
3. Kaskade (bei endgültiger Löschung):
- FeatureInstances → FeatureAccess → Instanz-Daten (Chats, Dokumente, Extraktionen)
- DataNeutralizerAttributes (Mapping-Tabelle)
- RAG-Index-Einträge mit `mandateId`
- UserMandate-Zuordnungen (User bleiben bestehen)
- MandateSubscription → Stripe-Subscription kündigen
- Mandate-Konfigurationen (Rollen, Settings)
- Mandate-Record
4. Batch-Job für endgültige Löschung nach Retention
**Akzeptanz:**
- Root-Mandate nicht löschbar
- Soft-Delete mit sofortiger Zugriffssperre
- Vollständige Kaskade ohne verwaiste Daten
- Stripe-Subscription wird gekündigt
**Abhängigkeiten:** AP 1.1
---
### AP 1.11 — `[FE]` Register-Seite: Zwei Flows
**Beschreibung:** Register-Formular unterstützt `type=personal` und `type=company` via Query-Parameter.
**Dateien:**
- `frontend_nyla/src/pages/Register.tsx``type` Query-Parameter
- `frontend_nyla/src/api/authApi.ts``registerApi` erweitern
**Schritte:**
1. Query-Parameter `type` auslesen (`personal` / `company`)
2. Bei `company`: Zusatzfeld „Firmenname" anzeigen
3. API-Call erweitern: `registrationType` + `companyName` im Body
4. Erfolgsseite anpassen: „Dein Mandant wurde erstellt"
5. Redirect nach Login auf Dashboard (nicht Store, da Instanzen via autoCreate existieren)
**Akzeptanz:**
- `/register?type=personal` zeigt Personal-Flow
- `/register?type=company` zeigt Company-Flow mit Firmenname
- API-Body enthält neue Felder
**Abhängigkeiten:** AP 1.4
---
### AP 1.12 — `[FE]` Login-Seite: Zwei CTAs + OAuth Onboarding-Wizard
**Beschreibung:** Login/Landing-Seite mit zwei prominenten Call-to-Actions und Onboarding-Wizard für OAuth-Erstregistrierungen.
**Dateien:**
- `frontend_nyla/src/pages/Login.tsx` — CTAs hinzufügen
- `frontend_nyla/src/components/OnboardingWizard.tsx` — NEU
**Schritte:**
1. Login-Seite: Zwei CTAs
- „Kostenlos testen" → `/register?type=personal`
- „Für Unternehmen" → `/register?type=company`
2. Onboarding-Wizard Komponente:
- Schritt 1: „Wie möchtest du PowerON nutzen?" (Personal / Unternehmen)
- Schritt 2: Bei Company → Firmenname eingeben
- Submit → `POST /api/onboarding/provision`
3. Nach OAuth-Login: Prüfen ob `requiresOnboarding=true` in Response → Wizard anzeigen
**Akzeptanz:**
- Zwei CTAs sichtbar auf Login-Seite
- OAuth-User ohne Mandate sieht Wizard
- Wizard erstellt Mandate und leitet zum Dashboard
**Abhängigkeiten:** AP 1.5
---
### AP 1.13 — `[FE]` Store-Seite: Mandanten-Auswahl
**Beschreibung:** Store zeigt explizite Mandanten-Auswahl bei Aktivierung. Alle User sehen den Store.
**Dateien:**
- `frontend_nyla/src/pages/Store.tsx` — Refactoring
**Schritte:**
1. Store für alle User zugänglich machen (kein Admin-Gate)
2. Pro Feature: Mandanten-Auswahl anzeigen (Dropdown bei Multi-Mandate, auto-select bei Solo)
3. „Aktivieren für [Mandantenname]" statt nur „Aktivieren"
4. Bei User ohne Admin-Mandate: „Eigenen Mandanten erstellen und Feature aktivieren" als Option
5. Deaktivierung: Pro Instanz mit Orphan-Warnung („Du bist der letzte User — Instanz wird gelöscht")
6. Anzeige aktiver Instanzen pro Mandate mit Instanz-Anzahl
**Akzeptanz:**
- Mandanten-Auswahl bei jeder Aktivierung
- Auto-Mandate-Option für User ohne eigenes Mandate
- Orphan-Warnung bei letzter Deaktivierung
**Abhängigkeiten:** AP 1.6, AP 1.7
---
### AP 1.14 — `[FE]` Subscription-Datenvolumen: UI-Hinweis
**Beschreibung:** Anzeige der Datenvolumen-Nutzung im Billing-Bereich.
**Dateien:**
- `frontend_nyla/src/pages/billing/BillingDataView.tsx` — Datenvolumen-Anzeige
**Schritte:**
1. API-Aufruf: `GET /api/mandate/{id}/data-usage`
2. Fortschrittsbalken: genutzt / maximal (z. B. „3.2 GB / 10 GB")
3. Warnung bei > 80%: „Du näherst dich dem Datenvolumen-Limit"
4. Upgrade-Link bei hoher Nutzung
**Akzeptanz:**
- Datenvolumen sichtbar im Billing-Bereich
- Warnung bei > 80%
- Kein Block, nur Hinweis
**Abhängigkeiten:** AP 1.8
---
### AP 1.15 — `[BE]` Bootstrap: Root-Mandant `mandateType=system` setzen
**Beschreibung:** Im Bootstrap den Root-Mandanten explizit auf `mandateType=system` setzen.
**Dateien:**
- `gateway/modules/interfaces/interfaceBootstrap.py``initRootMandate()` erweitern
**Schritte:**
1. In `initRootMandate()`: `mandateType=system` setzen
2. `initRootMandateFeatures()` bleibt für System-Instanzen (aber keine Endkunden-Instanzen mehr nach Migration)
**Akzeptanz:**
- Root-Mandant hat `mandateType=system` nach Bootstrap
- Keine Endkunden-Feature-Instanzen im Root (nach Migration)
**Abhängigkeiten:** AP 1.1, AP 1.9
---
## Phase 2 — UDB + Shared Data Scope
> Ziel: UDB als plattformweite Komponente, mandantenweiter Datenzugriff via Scopes. `FeatureInstance` = Datengrenze + mandantenweiter Zugriff.
> **Voraussetzung:** Phase 1 vollständig abgeschlossen.
---
### AP 2.1 — `[BE]` Datenmodell: Scope- und Neutralisierungs-Felder auf DataSources
**Beschreibung:** Bestehende Datenquellen-Modelle um `scope` und `neutralize` Felder erweitern. RAG-Index um Scope-Metadaten erweitern.
**Dateien:**
- Bestehende Datenmodelle (Dokumente, Extraktionen, Konnektoren) — `scope` + `neutralize` Felder
- RAG-Index-Konfiguration — Filterbare Metadaten
**Schritte:**
1. Enum `DataScope` definieren: `personal`, `featureInstance`, `mandate`, `global`
2. Felder hinzufügen: `scope: DataScope` (default `personal`), `neutralize: bool` (default `false`), `neutralizationStatus: str`
3. Bestehende Daten: Default `scope=featureInstance` (bestehendes Verhalten)
4. RAG-Index-Metadaten erweitern: `mandateId`, `scope`, `featureInstanceId`, `userId`, `isNeutralized`
5. Bestehende RAG-Einträge: Backfill mit Scope-Metadaten
**Akzeptanz:**
- Alle DataSources haben `scope` und `neutralize`
- RAG-Index filtert korrekt nach Scope
- Bestehende Daten haben konsistente Defaults
**Abhängigkeiten:** Phase 1 abgeschlossen
---
### AP 2.2 — `[BE]` RAG-Query: Scope-basierte Filterung
**Beschreibung:** RAG-Queries filtern Ergebnisse basierend auf dem effektiven Scope des anfragenden Users.
**Dateien:**
- RAG-Query-Logik — Scope-Filter aufbauen
**Schritte:**
1. Scope-Kontext aufbauen pro Query:
```
personal(userId) featureInstance(instanceId) mandate(mandateId) global()
```
2. Union-Query über vier Scopes implementieren
3. `scope=global` nur sysadmin-setzbar (RBAC-Check beim Tagging)
4. Performance-Tests mit grossen Datenmengen
**Akzeptanz:**
- User sieht nur Daten seiner Scopes
- Global-Scope RBAC-geschützt
- Query-Performance akzeptabel (< 500ms)
**Abhängigkeiten:** AP 2.1
---
### AP 2.3 — `[BE]` Datenquellen-Tagging API
**Beschreibung:** API-Endpoints für Scope- und Neutralisierungs-Tagging auf Datenquellen.
**Dateien:**
- Neue oder erweiterte Routes Tagging-Endpoints
**Schritte:**
1. `PATCH /api/datasources/{id}/scope` Scope ändern (personal featureInstance mandate; global nur sysadmin)
2. `PATCH /api/datasources/{id}/neutralize` Neutralisierungs-Flag togglen
3. Bei Scope-Änderung: RAG-Index re-indizieren (async)
4. Bei Neutralisierungs-Änderung: Re-Neutralisierung oder De-Neutralisierung triggern (async)
5. Defaults bei Upload: `scope=personal`, `neutralize=false`
**Akzeptanz:**
- Scope-Änderung löst Re-Indizierung aus
- Neutralisierungs-Änderung löst Re-Verarbeitung aus
- RBAC: `global` nur für sysadmin
**Abhängigkeiten:** AP 2.1, AP 2.2
---
### AP 2.4 — `[FE]` UDB: Extraktion aus Workspace
**Beschreibung:** Die bestehende Sidebar im AI Workspace (Chats, Files, Sources) wird als wiederverwendbare Plattformkomponente extrahiert.
**Dateien:**
- `frontend_nyla/src/pages/workspace/` Sidebar-Logik identifizieren und extrahieren
- `frontend_nyla/src/components/UnifiedDataBar/` NEU: UDB-Komponente
**Schritte:**
1. Bestehende Sidebar-Komponenten im Workspace identifizieren (Chats-Liste, Files-Liste, Sources-Liste)
2. Gemeinsame UDB-Komponente erstellen mit drei Tabs: Chats, Files, Sources
3. Props: `mandateId`, `featureInstanceId`, `userId` für Kontext
4. Workspace refactoren: UDB einbinden statt eigener Sidebar
5. Verifizieren: Workspace funktioniert identisch mit UDB-Komponente
**Akzeptanz:**
- UDB ist eine eigenständige Komponente in `src/components/`
- Workspace nutzt UDB und funktioniert wie zuvor
- Keine Duplizierung von Sidebar-Logik
**Abhängigkeiten:** Phase 1 abgeschlossen
---
### AP 2.5 — `[FE]` UDB: Chats-Tab mit Baumstruktur
**Beschreibung:** Chat-Verläufe als hierarchischer Baum, gruppiert nach Feature-Instanzen mit Suchfunktion und Flat Mode.
**Dateien:**
- `frontend_nyla/src/components/UnifiedDataBar/ChatsTab.tsx` NEU
**Schritte:**
1. API: Chats über alle Feature-Instanzen des Users laden (gruppiert)
2. Baumstruktur: Feature-Instanzen als Top-Level-Knoten, Chats darunter
3. Feature-spezifische Substrukturen (z. B. CommCoach Coaching-Modul Sessions)
4. Aktuelle Feature-Instanz: hervorgehoben und expandiert
5. Klick auf Chat: Navigation in den Chat (ggf. Feature-Kontext wechseln)
6. **Suchfunktion:** Volltextsuche über Chat-Titel, filtert Baum auf Treffer
7. **Flat Mode:** Toggle im Tab-Header, chronologische Liste sortiert nach letzter Aktivität
**Akzeptanz:**
- Alle Chats über alle Feature-Instanzen sichtbar in einem Baum
- Suche filtert korrekt
- Flat Mode zeigt chronologische Liste
- Klick navigiert zum Chat
**Abhängigkeiten:** AP 2.4
---
### AP 2.6 — `[FE]` UDB: Files-Tab mit Scope- und Neutralisierungs-Symbolen
**Beschreibung:** Dateien mit Inline-Scope-Symbolen und Neutralisierungs-Icons.
**Dateien:**
- `frontend_nyla/src/components/UnifiedDataBar/FilesTab.tsx` NEU
**Schritte:**
1. Dateien pro Mandat laden (alle Scopes, die der User sehen darf)
2. Pro Datei: Scope-Symbol (👤 personal, 👥 instanz, 🏢 mandant) + Neutralisierungs-Symbol (🔒)
3. Klick auf Scope-Symbol: Zyklisch wechseln oder Popover
4. Klick auf Neutralisierungs-Symbol: Toggle on/off
5. Default bei Upload: `personal` + kein 🔒
6. Legende am Tab-Footer
**Akzeptanz:**
- Alle Dateien mit korrekten Scope-Symbolen
- Scope- und Neutralisierungs-Toggle funktionieren mit API-Calls
- Visuell klar und unkompliziert
**Abhängigkeiten:** AP 2.3, AP 2.4
---
### AP 2.7 — `[FE]` UDB: Sources-Tab mit Active/Browse
**Beschreibung:** Datenquellen-Tab mit Trennung zwischen Active Sources (mit Icons) und Browse Sources (Katalog ohne Icons).
**Dateien:**
- `frontend_nyla/src/components/UnifiedDataBar/SourcesTab.tsx` NEU
**Schritte:**
1. Active Sources: Eingebundene Quellen mit Scope- und Neutralisierungs-Symbolen
2. Browse Sources: Verfügbare Quellen als Katalog (keine Symbole)
3. Feature Data: Daten pro Feature-Instanz (Tabellen-Übersicht)
4. Aktivierung: Drag aus Browse in Active oder Klick Default-Scope setzen
5. Deaktivierung: `x`-Button bei Active Sources
**Akzeptanz:**
- Klare Trennung Active vs. Browse
- Symbole nur bei Active Sources
- Aktivierung setzt Defaults korrekt
**Abhängigkeiten:** AP 2.3, AP 2.4
---
### AP 2.8 — `[FE]` UDB: Drag-and-Drop in Prompt
**Beschreibung:** Alle UDB-Elemente (Chats, Files, Sources) können per Drag-and-Drop in den Prompt gezogen werden. Bei Chats werden die RAG-Daten des Chats abgefragt, nicht der Chat-Inhalt.
**Dateien:**
- `frontend_nyla/src/components/UnifiedDataBar/` DnD-Logik
- Prompt-Eingabe-Komponente Drop-Zone
**Schritte:**
1. Drag-Source auf allen UDB-Elementen (Chats, Files, Sources)
2. Drop-Zone im Prompt-Bereich
3. **Chats:** Bei Drop RAG-Query nach Daten, die dem Chat zugeordnet sind (nicht Chat-Inhalt selbst)
4. **Files:** Bei Drop Datei als Kontext-Dokument anhängen
5. **Sources:** Bei Drop Datenquelle als aktiven Kontext setzen
6. Visuelles Feedback: Drag-Preview, Drop-Highlight, Kontext-Badge im Prompt
**Akzeptanz:**
- Drag-and-Drop funktioniert für alle drei Tabs
- Chats liefern RAG-Daten, nicht Chat-Inhalt
- Visuelles Feedback bei Drag und Drop
**Abhängigkeiten:** AP 2.5, AP 2.6, AP 2.7
---
### AP 2.9 — `[FE]` UDB: Integration in alle Feature-Instanzen
**Beschreibung:** UDB in alle bestehenden Features einbinden (Workspace, CommCoach, Trustee, Automation).
**Dateien:**
- Feature-Seiten: Workspace, CommCoach, Trustee, Automation UDB einbinden
**Schritte:**
1. Workspace: UDB ersetzt bestehende Sidebar (bereits in AP 2.4)
2. CommCoach: UDB als Sidebar einbinden
3. Trustee: UDB als Sidebar einbinden
4. Automation: UDB als Sidebar einbinden
5. Pro Feature: Korrekter Kontext (`featureInstanceId`) an UDB übergeben
6. Layout-Anpassungen pro Feature
**Akzeptanz:**
- Alle Features haben die UDB
- Kontext stimmt pro Feature-Instanz
- Keine Feature-eigenen Sidebar-Duplikate mehr
**Abhängigkeiten:** AP 2.4 AP 2.8
---
### AP 2.10 — `[BE]` Doppelte Strukturen konsolidieren
**Beschreibung:** Datensilos auflösen: Voice-Definitionen, Dokumentenkontext und Wissensbasis mandantenweit verfügbar machen.
**Dateien:**
- CommCoach Voice-Datenmodell Scope-Feld hinzufügen
- Workspace Dokument-Logik Scope-basierte Queries
**Schritte:**
1. Voice-Definitionen: Von instanz-zentriert auf mandantenweit umstellen (scope=mandate)
2. Wissensbasis: Von instanz-zentriert auf scope-basiert (User wählt Scope)
3. Dokumenten-Kontext: Mandantenweite Suche mit Scope-Filter
4. Feature-spezifische Daten-Tabellen: `scope`-Feld wo nötig
5. Migration bestehender Daten: Default-Scope setzen
**Akzeptanz:**
- Voice-Definitionen mandantenweit nutzbar
- Keine doppelten Definitionen mehr nötig
- Cross-Feature-Zugriff auf Wissensbasis funktioniert
**Abhängigkeiten:** AP 2.1, AP 2.2
---
## Phase 3 — Unified Data Layer + Neutralisierung als Kerndisziplin
> Ziel: Features sind reine Workflow-Oberflächen. UDL mit Neutralisierung ist der Plattformkern. `FeatureInstance` = UI-Scope.
> **Voraussetzung:** Phase 2 vollständig abgeschlossen.
---
### AP 3.1 — `[BE]` Neutralisierung: Fail-Safe-Logik
**Beschreibung:** Wenn Neutralisierung fehlschlägt, wird das Dokument nicht weitergegeben. Kein Fallback auf Originaldaten.
**Dateien:**
- `gateway/modules/workflows/methods/methodContext/actions/neutralizeData.py` Fail-Safe einbauen
- `gateway/modules/features/neutralization/serviceNeutralization/` Error Handling
**Schritte:**
1. Bestehende Error-Handling-Logik ändern: Bei Fehler **nicht** das Original-Part verwenden
2. Stattdessen: Part überspringen + Warning-Flag setzen
3. ChatWorkflow: Hinweis Dokument X konnte aus Datenschutzgründen nicht einbezogen werden"
4. AI-Call läuft ohne das fehlgeschlagene Dokument weiter
5. Logging: Fehlergrund (Timeout, Modell nicht erreichbar, etc.)
6. Beim RAG-Indexing: Neutralisierung fehlgeschlagen Dokument nicht indizieren, `neutralizationStatus=failed`
**Akzeptanz:**
- Kein Originaldokument geht an externe AI wenn Neutralisierung fehlschlägt
- User sieht Hinweis, welches Dokument fehlt und warum
- AI-Call funktioniert ohne das fehlende Dokument
**Abhängigkeiten:** Phase 2 abgeschlossen
---
### AP 3.2 — `[BE]` Neutralisierung: RAG-Integration
**Beschreibung:** Datenquellen mit `neutralize=true` werden nur in neutralisierter Form im RAG indiziert.
**Dateien:**
- RAG-Indexing-Pipeline Neutralisierungs-Gate
- `gateway/modules/features/neutralization/` Pipeline-Integration
**Schritte:**
1. Beim Einbinden einer Datenquelle mit `neutralize=true`:
- Container auflösen (bestehende Extraction-Pipeline)
- Media droppen
- Bilder neutraler Text via Modell mit `opType=neutralization`
- Text neutralisieren via `StringParser` + Modell mit `opType=neutralization`
- Platzhalter-Mapping in `DataNeutralizerAttributes` persistieren
- **Nur neutralisierte Version** im RAG indizieren
2. Bei `neutralize=false`: Direkt indizieren (wie bisher)
3. Re-Neutralisierung bei Flag-Änderung (async Batch-Job)
**Akzeptanz:**
- Neutralisierte Datenquellen: nur neutralisierte Version im RAG
- Mapping-Tabelle wird korrekt befüllt
- Re-Neutralisierung bei Flag-Änderung funktioniert
**Abhängigkeiten:** AP 3.1, AP 2.1
---
### AP 3.3 — `[BE]` Neutralisierung: AI-Call-Integration + Re-Hydrierung
**Beschreibung:** Bei AI-Calls mit `requireNeutralization=true`: Prompt live neutralisieren und Response re-hydrieren.
**Dateien:**
- Workflow-Manager / Chat-Pipeline Pre-/Post-Processing
- `gateway/modules/workflows/workflowManager.py`
**Schritte:**
1. Pre-Processing: Wenn `requireNeutralization=true`:
- User-Prompt durch Neutralisierungs-Pipeline
- RAG-Kontext-Dokumente: bereits neutralisiert (aus RAG-Index)
- Prompt-Mapping in temporärer Tabelle
2. AI-Call mit neutralisiertem Prompt + neutralisierten Dokumenten
3. Post-Processing: AI-Response re-hydrieren
- Platzhalter via Mapping-Tabelle durch Originale ersetzen
- Konsistenz: `[PERSON_1]` = immer derselbe Originalwert (persistent pro Datenquelle)
4. Re-hydrierte Response an User ausliefern
**Akzeptanz:**
- Live-Neutralisierung von User-Prompts funktioniert
- Re-Hydrierung ersetzt Platzhalter korrekt
- Platzhalter-Konsistenz über Calls hinweg
**Abhängigkeiten:** AP 3.1, AP 3.2
---
### AP 3.4 — `[FE]` ChatWorkflow-Transparenz: „Gesendete Daten"
**Beschreibung:** Im Chat-UI: aufklappbarer Bereich, der die neutralisierten Dokumente zeigt, die an das AI-Modell gesendet werden.
**Dateien:**
- Chat-UI-Komponente Neue Sektion Gesendete Daten (neutralisiert)"
**Schritte:**
1. API liefert `ActionDocument`-Liste mit `neutralized=true` Metadaten im ChatWorkflow
2. UI: Aufklappbarer Bereich unter jedem AI-Call
3. Anzeige: Dokumentname, Neutralisierungs-Status, Vorschau des neutralisierten Texts
4. Hinweis wenn Dokument übersprungen wurde (Fail-Safe)
5. Link zur Neutralisierungs-Konfiguration der Datenquelle
**Akzeptanz:**
- User sieht bei jedem AI-Call, was rausgegangen ist
- Übersprungene Dokumente sind sichtbar mit Grund
- Aufklappbar (nicht standardmässig offen)
**Abhängigkeiten:** AP 3.3
---
### AP 3.5 — `[FE]` Neutralisierung: User-Kontrolle
**Beschreibung:** UI für Einsicht und Verwaltung der neutralisierten Daten und Platzhalter-Mappings.
**Dateien:**
- Neue Seite oder Sektion im Settings-Bereich
**Schritte:**
1. Übersicht: Alle Datenquellen mit Neutralisierungs-Status
2. Pro Datenquelle: Detailansicht der Platzhalter-Mappings (Original Platzhalter)
3. Löschen: Einzelne Mappings oder alle Mappings einer Datenquelle
4. Status: `pending`, `completed`, `failed`, `not_required`
5. Re-Trigger: Neutralisierung manuell neu anstossen
**Akzeptanz:**
- User kann alle Mappings einsehen
- Löschen funktioniert
- Status ist korrekt und aktuell
**Abhängigkeiten:** AP 3.2
---
### AP 3.6 — `[BE]` RAG-Scopes: Vollständige Implementierung
**Beschreibung:** Alle vier Scopes (personal, featureInstance, mandate, global) vollständig implementiert mit korrekter Union-Query und RBAC.
**Dateien:**
- RAG-Query-Engine vollständige Scope-Logik
- RBAC-Layer Scope-Berechtigungen
**Schritte:**
1. `personal`: Nur Daten des anfragenden Users
2. `featureInstance`: Alle Daten der aktuellen Instanz (alle User mit FeatureAccess)
3. `mandate`: Alle Daten aller Instanzen im Mandat
4. `global`: Alle plattformweiten Daten (read-only, nur sysadmin setzbar)
5. Union-Query: Effiziente Kombination aller anwendbaren Scopes
6. Caching: Scope-Metadaten cachen für Performance
**Akzeptanz:**
- Alle Scope-Kombinationen funktionieren korrekt
- User sieht nie Daten ausserhalb seiner Berechtigung
- Performance: Union-Query < 500ms
**Abhängigkeiten:** AP 2.2, AP 3.2
---
### AP 3.7 — `[FE]` Onboarding-Assistant
**Beschreibung:** Kontextsensitiver Onboarding-Begleiter für neue User und leere Zustände.
**Dateien:**
- `frontend_nyla/src/components/OnboardingAssistant.tsx` NEU
**Schritte:**
1. Trigger-Bedingungen: Kein Mandate, keine Instanzen, keine Chats, nach Pause
2. **Schritt 1:** Mandant-Typ bestätigen (bei OAuth)
3. **Schritt 2:** Erste Feature-Instanz aktivieren (Empfehlung: Workspace)
4. **Schritt 3:** Erste Datenquelle einbinden (Upload oder Konnektor)
5. **Schritt 4:** Erster AI-Call (geführter Prompt)
6. Kontextsensitiv: Erscheint bei neuen Features, nach Pausen, bei leeren Zuständen
7. Dismissable: User kann jederzeit überspringen, aber Assistant kommt bei relevantem Trigger zurück
**Akzeptanz:**
- Neue User durchlaufen den 4-Schritt-Flow
- Assistant erscheint kontextsensitiv
- Kein Nerv-Faktor (smart triggers, nicht bei jedem Login)
**Abhängigkeiten:** AP 2.9
---
### AP 3.8 — `[BE/FE]` FeatureInstance als UI-Scope (Semantische Verschiebung)
**Beschreibung:** Features werden zu reinen Workflow-Oberflächen. Die `FeatureInstance` definiert den UI-Kontext, nicht mehr die Datengrenze. Der Unified Data Layer ist die Datengrenze.
**Dateien:**
- Feature-Logik in allen Features Daten-Queries auf UDL umstellen
- Instanz-Referenzen von Daten-Ownership auf UI-Scope
**Schritte:**
1. Daten-Queries: Von `WHERE featureInstanceId = X` auf Scope-basierte RAG-Queries umstellen
2. `featureInstanceId` bleibt als Herkunftsreferenz (Quellenschutz), steuert aber nicht mehr den Zugriff
3. Zugriff wird über Scope + RBAC gesteuert
4. Feature-Instanz liefert: Workflow-Oberfläche, Feature-spezifische Substrukturen, Default-Scope-Kontext
5. Alle Features verifizieren: Datenfluss via UDL statt Instanz-Silo
**Akzeptanz:**
- Features zeigen Daten aus dem UDL (nicht nur eigene Instanz)
- Cross-Feature-Datennutzung funktioniert
- `featureInstanceId` dient nur noch als Herkunftsinfo
**Abhängigkeiten:** AP 3.6, AP 2.9, AP 2.10
---
## Zusammenfassung
| Phase | Arbeitspakete | Schwerpunkt |
|-------|--------------|-------------|
| **Phase 1** | AP 1.1 1.15 (15 APs) | Mandanten-Grenze, Registrierung, Store, Migration, Löschung |
| **Phase 2** | AP 2.1 2.10 (10 APs) | UDB, Scope-Tagging, RAG-Filter, Cross-Feature-Zugriff |
| **Phase 3** | AP 3.1 3.8 (8 APs) | Neutralisierung, RAG-Scopes, Onboarding-Assistant, UI-Scope-Shift |
| **Total** | **33 Arbeitspakete** | |
### Abhängigkeitsgraph (kritischer Pfad)
```
Phase 1:
AP 1.1 (mandateType) ──┬── AP 1.2 (Subscription PENDING) ── AP 1.3 (_provision)
│ │
│ ┌──────────────────────────────────────┘
│ │
├── AP 1.4 (Register Local) ── AP 1.11 (FE Register)
│ │
├── AP 1.5 (Register OAuth) ── AP 1.12 (FE Login + Wizard)
│ │
├── AP 1.6 (Store Refactoring) ── AP 1.7 (Auto-Mandate) ── AP 1.13 (FE Store)
├── AP 1.8 (Subscription Volume) ── AP 1.14 (FE Volume)
├── AP 1.9 (Migration Script) ── AP 1.15 (Bootstrap)
└── AP 1.10 (Mandanten-Löschung)
Phase 2: (nach Phase 1)
AP 2.1 (Scope-Felder) ── AP 2.2 (RAG Scope-Filter) ── AP 2.3 (Tagging API)
AP 2.4 (UDB Extraktion) ── AP 2.5 (Chats) ─┐ │
AP 2.6 (Files) ──┤── AP 2.8 (DnD) ── AP 2.9 (Integration)
AP 2.7 (Sources)─┘
AP 2.10 (Konsolidierung) ─────────────────────────────────────────┘
Phase 3: (nach Phase 2)
AP 3.1 (Fail-Safe) ── AP 3.2 (RAG-Integration) ── AP 3.3 (AI-Call + Rehydrate) ── AP 3.4 (FE Transparenz)
└── AP 3.5 (FE User-Kontrolle)
AP 3.6 (RAG-Scopes komplett) ── AP 3.8 (UI-Scope-Shift)
AP 3.7 (Onboarding-Assistant)
```
---
*Umsetzungsplan-Version: 2026-03-23 v1 — Basierend auf Konzeptdokument v4. 33 Arbeitspakete in 3 Phasen.*