# 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.*