wiki/c-work/4-done/2026-05-rag-consent-and-control-unification.md
2026-06-02 09:42:12 +02:00

38 KiB
Raw Blame History

Unified RAG Consent, Neutralization and Visibility — Single Source of Truth

Beschreibung und Kontext

Mit dem Plan 2026-04-id-unified-knowledge-indexing-rag-concept.md (P0P1d) wurde der technische Backend-Pfad für RAG-Ingestion implementiert (Connection-OAuth → Bootstrap → Walker → requestIngestioninterfaceDbKnowledge) und der AddConnectionWizard ausgeliefert (Consent + Preferences + Kostenabschätzung).

Bei der Architektur-Review (2026-05-12) wurde jedoch festgestellt:

  • Wizard ist standalone, ohne geschlossenen Lebenszyklus für seine Setzungen.
  • Consent ohne Revoke-UI — User kann „Wissensdatenbank: Ja" wählen, aber nirgends widerrufen ohne die Connection zu löschen.
  • Doppelte Neutralisierungs-Quelle: UserConnection.knowledgePreferences.neutralizeBeforeEmbed (global pro Connection, gesetzt im Wizard) und DataSource.neutralize (UDB, pro Pfad). Beide wirken unabhängig — verwirrt den User.
  • Surface Toggles und MIME-Allowlist sind tot: Werden geparst, aber in keinem Walker referenziert.
  • GET /api/connections/ zeigt Knowledge-Felder nicht. Selbst wenn UI gebaut wäre, fehlt die Datenquelle.
  • Bootstrap-Job-Status für User unsichtbar: Bootstrap-Jobs ohne mandateId/api/jobs blockt. User klickt „Verbinden" und sieht kein Feedback.
  • Indexierung kann nicht gestoppt werden: Wenn ein Bootstrap einer 50'000-Item-Mailbox losläuft, gibt es keinen Stop-Button. Kein Cost-Cap.
  • WorkspaceRagInsightsPage am falschen Ort: Workspace-instance-scoped Stats für ein plattformweites Konzept (page.system.ragInventory wäre richtig).
  • „Was ist neu zu indexieren" ist undurchsichtig: Walker walked alles, dedupliziert via Content-Hash. Es ist korrekt, aber für den User nirgends erklärt.
  • Walker-Scope ist OAuth-global, nicht user-gesteuert. User kann nicht wählen „SharePoint-Site A ja, Site B nein".

Business-Treiber: DSGVO-Compliance (Art. 7: Widerruf so einfach wie Erteilung), Kosten-Kontrolle (Stop-fähig, scope-fähig), und Vertrauen (Transparenz: was ist drin, wann reinprozessiert, wie raus).

Fokus und kritische Details

Architektonisches Leitprinzip (vom User formuliert)

„Der Wizard ist nur eine UI-Komponente. Dies ist keine Architektur. Die Architektur muss im Backend datenzentriert sein und jeder Schritt muss transparent sein und im UI für den User ersichtlich."

Daraus ergeben sich vier harte Regeln:

  1. Jede Einwilligung im Wizard MUSS an genau einem Ort im UI revozierbar sein. „Knowledge Ingestion" on/off muss in ConnectionsPage und auf der RAG-Inventar-Seite umstellbar sein, mit demselben Backend-Effekt wie Connection-Revoke (Purge).
  2. Neutralisierung wird AUSSCHLIESSLICH in der UDB pro Tree-Element verwaltet. Es darf keine „Neutralisierung global einer Datenquelle" geben, die „einfach so" wirkt. UserConnection.knowledgePreferences.neutralizeBeforeEmbed ist Anti-Pattern und wird entfernt.
  3. Was im RAG ist, muss der User sehen UND auf der Granularität entfernen können, auf der er es hinzugefügt hat.
  4. Indexierung muss jederzeit stoppbar sein — sowohl als „Stopp-Knopf" auf laufenden Jobs als auch als impliziter Stop durch das Entfernen des Index-Toggles in der UDB.

Kern-Idee: Per-Tree-Element-Toggle (analog zu Scope und Neutralize)

Heute in der UDB hat jedes Tree-Element drei Aktionen-Buttons: 💬 Chat / 🧑 Scope-Cycle / 🔒 Neutralize-Toggle. Die DataSource-Row materialisiert sich, sobald der User eine dieser Aktionen für einen Pfad konfiguriert (scope, neutralize).

Morgen kommt ein vierter Button hinzu: 🧠 RAG-Index-Toggle. Selbe Mechanik:

  • Toggle aktivieren → DataSource bekommt ragIndexEnabled = true → wird beim nächsten Bootstrap/Resync indexiert
  • Toggle deaktivieren → ragIndexEnabled = false + sofortiger Purge-Job für diesen Pfad
  • Damit ist der Index-Lebenszyklus strukturidentisch mit Scope und Neutralize. Konsistent und intuitiv.

Konsequenz: Walker iterieren nicht mehr OAuth-global. Sie iterieren über DataSource-Rows mit ragIndexEnabled = true der jeweiligen Connection. Der User entscheidet explizit pro Tree-Element was indexiert wird.

Granularität: Tree-Kaskade vs. Tabellen-Felder

Die UDB unterscheidet schon heute zwei Daten-Typologien — beide Policies (neutralize, scope, künftig ragIndexEnabled) müssen sich konsistent dazu verhalten:

Daten-Typ Beispiele Wie wirken Policies
Tree (DataSource) SharePoint-Folder, Outlook-Mail-Folder, Drive-Folder, ClickUp-Liste Kaskadierend: Eine Policy auf einem Container-Knoten (Folder/Site) vererbt sich auf alle Kinder, bis ein Kind eine eigene Policy explizit überschreibt. Code-Pattern existiert bereits (inheritedScope, inheritedNeutralize in SourcesTab.tsx). ragIndexEnabled muss denselben Vererbungs-Mechanismus nutzen — sonst inkonsistent.
Tabular (FeatureDataSource) Trustee-Positionen-Tabelle, RealEstate-Bauvorschriften, beliebige Feature-DATA_OBJECT-Tabellen Pro Feld: FeatureDataSource.neutralizeFields: List[str] — User kann sagen „indexier diese Tabelle, aber email-Feld neutralisieren, name-Feld lassen". Bestehender Code: _toggleNeutralizeField in SourcesTab.tsx. Für ragIndexEnabled auf Tabellen-Ebene ist Per-Feld-Granularität nicht sinnvoll (entweder Zeile geht in RAG oder nicht) — also: ragIndexEnabled gilt pro Tabelle/FeatureDataSource, nicht pro Feld.

Verbindliche Regeln:

  1. neutralize auf Container vererbt sich nach unten, bis ein Child-Knoten einen eigenen neutralize-Wert hat (true oder false). „Inherited"-Visualisierung im UI bleibt wie heute (gestrichelt / gedimmt).
  2. ragIndexEnabled auf Container vererbt sich gleich: Toggle auf einem SharePoint-Site-Knoten = alle Subfolders+Files werden indexiert (mit der vererbten neutralize-Policy), ausser ein Subfolder-Knoten setzt explizit ragIndexEnabled = false.
  3. Tabellarisch (FeatureDataSource): neutralizeFields: List[str] ist die einzige Granularitäts-Stufe für Neutralisierung. ragIndexEnabled gilt pro Tabelle. Wenn der User bestimmte Spalten gar nicht im RAG haben will, ist die Lösung „Tabelle nicht indexieren" oder ein neuer excludeFields: List[str] (Roadmap-Item, nicht in v1).
  4. Walker-Implementierung muss die Vererbung respektieren: Wenn User SharePoint-Site indiziert (Container) und nicht jeden Subfolder einzeln, durchläuft der Walker die Tree-Hierarchie und nutzt für jedes File die effektive Policy (eigene > geerbt > default false).

Vorteile:

  • Granularität: User wählt „SharePoint Site A → Folder 'Reports' indexieren, Folder 'Drafts' nicht".
  • Revoke trivial: Ein Klick auf den Toggle in der UDB → automatischer Purge.
  • Einheitliche UX-Pattern (gleiche Iconisierung wie Scope/Neutralize).
  • Kein „Auto-Default-DataSources beim Connect"-Hack — User muss aktiv was wählen, was DSGVO-konformer ist (kein Default-Indexing ohne explizite Wahl).
  • DataSource braucht kein separates autoSync/autoIndex-Attribut auf Row-Level — die ragIndexEnabled-Spalte IST die Speicherung; das Icon IST die Bedienung.

Konsequenz für knowledgeIngestionEnabled auf der Connection:

  • Bleibt als rechtlicher Master-Schalter („darf PowerOn überhaupt etwas aus dieser Connection in die Wissensdatenbank legen?").
  • Wenn false: Kein Walker startet, egal welche ragIndexEnabled-DataSources existieren. Existierende Chunks werden gepurged.
  • Wenn true: Walker iteriert über DataSources mit ragIndexEnabled = true. Wenn keine existiert → Walker macht nichts (kein leerer Default-Walk).

Indexierung jederzeit stoppbar

Drei Stop-Mechanismen:

  1. Implizit pro Element: In der UDB den Index-Toggle einer DataSource ausschalten → Purge dieser Items + Walker überspringt sie beim nächsten Lauf.
  2. Implizit pro Connection: Master-Toggle auf der Connection ausschalten (Knowledge-Consent revoke) → Purge aller Chunks dieser Connection + nächste Bootstrap-/Resync-Jobs werden gar nicht erst gestartet.
  3. Explizit pro laufendem Job: Stop-Button in der RAG-Inventar-Seite und auf der ConnectionsPage, sobald ein Bootstrap-Job RUNNING ist. Backend setzt Job-Status auf CANCELLED; Walker prüft alle ~50 Items das Cancel-Flag und beendet graceful.
    • Bereits indexierte Items bleiben drin (kein Rollback). Wer komplett raus will → Master-Toggle off.
    • BackgroundJobStatusEnum.CANCELLED existiert bereits im Enum — fehlt nur die cancelJob()-API + Cancel-Check im Walker-Loop.

„Was wird neu indexiert?" — Dedup-Mechanismus transparent gemacht

Die Frage taucht beim User auf: „Wenn ich in 24h meinen täglichen Resync laufen lasse — wie weiss das System was neu ist?"

Die Antwort existiert technisch bereits, ist aber nirgends im UI sichtbar gemacht:

Ebene Mechanismus Im Code
Pro Item, vor API-Call (Cheap-Skip) Walker speichert externen Revisions-Token (eTag für SharePoint, modifiedTime für Drive, historyId für Gmail, changeKey für Outlook, date_updated für ClickUp). Wenn unverändert → Walker skippt den API-Download. Bootstrap-Walker, Provider-Adapter
Pro Item, nach Extraction (Embedding-Skip) requestIngestion baut Content-Hash über (contentType, data, extractor-order) — Pure Function des Inhalts. Wenn FileContentIndex.structure._ingestion.hash matcht → Status bleibt indexed, kein neues Embedding. mainServiceKnowledge.requestIngestion
Bei Connection-Revoke deleteFileContentIndexByConnectionId purged alle Rows mit connectionId = X. interfaceDbKnowledge
Bei DataSource-Revoke (NEU in diesem Plan) deleteFileContentIndexByConnectionAndPathPrefix purged Rows mit connectionId = X und contextRef.path startet mit Y. NEU

Daraus folgt für den Plan:

  • Keine neue Mechanik nötig für „was ist neu" — die Hash-Idempotenz reicht.
  • UI-Aufgabe: Auf der RAG-Inventar-Seite muss klar dokumentiert/sichtbar sein: „Letzter Sync: vor 18h. Neue Items werden täglich um 02:00 ergänzt. Unveränderte Items werden nicht erneut verarbeitet."
  • Pro Sync sichtbare Counter: indexedNew, skippedDuplicate, failed (existieren als Log-Events; müssen pro Connection auf der Inventar-Seite aggregiert werden).

Konsequenzen für die Datenmodellierung

Heute Wird zu
UserConnection.knowledgeIngestionEnabled bleibt — rechtlicher Master-Gate, mit Revoke-UI
UserConnection.knowledgePreferences.neutralizeBeforeEmbed entfernen — Migration: bei true werden alle existierenden DataSources der Connection auf neutralize=true gesetzt
UserConnection.knowledgePreferences.{mailContentDepth, mailIndexAttachments, filesIndexBinaries, clickupScope, maxAgeDays} bleiben als Connection-weite Default-Sync-Einstellungen, Edit-UI in Connection-Detail-Drawer
UserConnection.knowledgePreferences.{mimeAllowlist, surfaceToggles} entfernen — durch Per-Element-Toggle obsolet
DataSource.neutralize bleibt — einzige Quelle für Neutralisierungs-Policy pro Tree-Element
DataSource.scope bleibt unverändert
DataSource.autoSync wird umbenannt zu ragIndexEnabled (oder bleibt + Doku — Entscheidung in Phase A) — neuer Sinn: „dieses Tree-Element wird in den RAG indexiert"
WorkspaceRagInsightsPage (Workspace-scoped) gelöscht, ersetzt durch globale Start > Nutzung > RAG-Inventar
/api/workspace/{instanceId}/rag-statistics gelöscht, ersetzt durch /api/rag/inventory/{me|mandate|platform}
BackgroundJob ohne Cancel-API erweitert: cancelJob(jobId), Walker-Cancel-Check

Wizard-Vereinfachung — alle Connector-Typen über denselben Pfad

Der AddConnectionWizard ist die einzige Eintragungs-UI für neue Connections. Alle heute parallelen Buttons auf der ConnectionsPage (separater „Infomaniak"-Button mit eigener Modal-Logik, separater „Admin-Zustimmung"-Button für Microsoft) werden in den Wizard integriert. Auf der ConnectionsPage bleibt nur „Verbindung hinzufügen" als Eintrittspunkt.

Connector-Type-Aware Wizard: Der Wizard hat eine Basis-Sequenz und typ-spezifische Zusatz-Schritte, die nach „Anbieter wählen" eingehängt werden.

Basis-Sequenz (alle Typen)

  1. Anbieter wählen — Cards für Google / Microsoft 365 / ClickUp / Infomaniak (Infomaniak kommt also wieder in die Auswahl).
  2. (typ-spezifischer Zusatz-Schritt — falls vorhanden, siehe unten)
  3. Rechtliche Einwilligung (Knowledge Ingestion ja/nein) — kurzer Hinweistext: „Du wählst nach dem Verbinden in der Datenleiste konkret, welche Ordner/Postfächer/Listen indexiert werden sollen."
  4. Verbindungsschritt — typ-abhängig: OAuth-Popup (google/msft/clickup) oder PAT-Eingabe (infomaniak).

Connector-Type-spezifische Zusatz-Schritte

Connector Zusatz-Schritt nach „Anbieter wählen" Inhalt
Microsoft 365 Optional: Admin-Zustimmung (Tenant-weit) Hinweis: „Falls dein Tenant einen Admin-Consent erfordert, kann er vor der persönlichen OAuth einmalig erteilt werden. Sonst überspringe diesen Schritt." Button öffnet /api/msft/adminconsent Popup. Nach Schliessen → weiter zu Step 3.
Google (Standard OAuth)
ClickUp (Standard OAuth)
Infomaniak Pflicht: Personal Access Token Inline-Eingabefeld + Erklärung wie heute im infomaniakModal. Erst nach valider Token-Validierung → weiter zu Step 3. Connection wird beim PAT-Submit erstellt (vergleichbar mit createInfomaniakConnection + submitInfomaniakToken); kein OAuth-Popup.

Mechanik: Der Wizard hält pro Connector-Typ eine kleine Step-Definition (z.B. WIZARD_STEPS_BY_TYPE: Record<ConnectorType, WizardStep[]>), die der Reihe nach durchlaufen wird. Stepper im UI passt sich dynamisch an (z.B. 4 Punkte für Microsoft mit Admin-Zustimmung, 3 für Google).

Entfernt von ConnectionsPage:

  • Button „Admin-Zustimmung" — wandert in den Wizard (Microsoft-Pfad).
  • Button „Infomaniak" — wandert in den Wizard (eigener Connector-Typ in der Anbieter-Auswahl).
  • infomaniakModal-State — wandert in den Wizard.
  • handleAdminConsent — wandert in den Wizard.

Entfernt aus dem heutigen Wizard:

  • Step 2 (Preferences mit Neutralisierung-Checkbox, Mail-Tiefe etc.) — wandert in Connection-Detail-Drawer bzw. in die UDB (Neutralize per Element).
  • Kostenabschätzung — wird komplett entfernt. Begründung: Schätzung war unzuverlässig (Token-Annahmen pro Mail spekulativ; Drive/SharePoint-Datenmenge nicht bekannt vor dem ersten Walk), erzeugt falsche Sicherheit, und stoppt User unnötig vom Connect. Kostenkontrolle erfolgt nachgelagert: über die Stop-Funktion und über das Per-Element-Opt-In.

Vorteil: Genau ein Eintrittspunkt für jeden Connector-Typ. Konsistente UX. Die echte Konfiguration (was indexieren, was neutralisieren) passiert dort, wo der User die Daten sieht — in der UDB.

Wo sieht und stoppt der User die Indexierung? (Sichtbarkeit-Spec)

Diese Frage muss drei UI-Plätze ohne Lücke abdecken:

Ort Was sichtbar Aktionen
Globales Header-Indikator (klein, oben rechts neben User-Menü) Badge mit Anzahl gerade laufender Bootstrap-/Resync-Jobs des aktuellen Users (z.B. „🔄 2"). Klick → öffnet RAG-Inventar-Seite. Verschwindet, wenn keine Jobs laufen. Polling: alle 510s. Click-through zur RAG-Inventar
ConnectionsPage, pro Row Pro Connection mit aktivem Bootstrap-Job: Status-Pill „Indexierung läuft" + Progress (processed / known total oder Prozent) + Roter Stop-Button. Bei finished: „Letzter Sync: , X indexiert / Y skipped / Z failed". Stop-Button → POST /api/connections/{id}/knowledge-stop. Master-Toggle (off → Confirm → Purge + Cancel).
RagInventoryPage Tab „Meine Daten" Vollständige Sicht: pro Connection eine Card; pro Card alle indexierten DataSources mit Item-Count + letztem Sync; Status-Pill bei laufendem Bootstrap; pro DataSource der UDB-Index-Toggle nochmals als Quick-Off. Stop-Button pro Connection (gleiche API). Toggle off pro DataSource → Mini-Purge. Master-Toggle der Connection. „Jetzt synchronisieren"-Button → enqueue Bootstrap on-demand.

Backend-Voraussetzung für die Sichtbarkeit: GET /api/rag/connection/{id}/status liefert für eine Connection { runningJobId?: string, runningJobProgress?: number, lastRun?: { finishedAt, indexedNew, skippedDuplicate, failed }, nextScheduledResync?: timestamp }. GET /api/rag/inventory/me aggregiert das für alle Connections und liefert zusätzlich runningJobsTotal für das Header-Badge.

Kritische Stellen im Code

  • platform-core/modules/serviceCenter/services/serviceKnowledge/subConnectorIngestConsumer.py — Bootstrap-Dispatcher: muss Walker auf DataSource-Iteration umstellen + Cancel-Flag prüfen.
  • platform-core/modules/serviceCenter/services/serviceKnowledge/subConnectorSync*.py (5 Walker) — konsumieren DataSource-Liste statt globalem Authority-Walk; lesen neutralize aus DataSource; prüfen periodisch cancelRequested.
  • platform-core/modules/serviceCenter/services/serviceKnowledge/subConnectorPrefs.pyneutralizeBeforeEmbed, mimeAllowlist, surfaceToggles raus.
  • platform-core/modules/datamodels/datamodelUam.pyknowledgePreferences Schema-Doku anpassen.
  • platform-core/modules/datamodels/datamodelDataSource.pyautoSyncragIndexEnabled (oder umdokumentieren); ggf. lastIndexed-Feld hinzufügen.
  • platform-core/modules/serviceCenter/services/serviceBackgroundJobs/mainBackgroundJobService.pycancelJob(jobId) API hinzufügen; JobProgressCallback um isCancelled() erweitern.
  • platform-core/modules/routes/routeDataConnections.pyGET liefert Knowledge-Felder; neue PATCH-Endpoints für Consent + Preferences + Stop.
  • platform-core/modules/routes/routeDataSources.py — neuer PATCH /{id}/rag-index mit Purge bei Off-Toggle.
  • platform-core/modules/interfaces/interfaceDbKnowledge.pydeleteFileContentIndexByConnectionAndPathPrefix(connectionId, pathPrefix) und listFileContentIndexByConnection(connectionId) und listFileContentIndexByDataSource(dataSourceId).
  • platform-core/modules/features/workspace/routeFeatureWorkspace.pyrag-statistics Methode löschen.
  • NEU platform-core/modules/routes/routeRagInventory.py/api/rag/inventory/{me,mandate,platform} und /api/rag/connection/{id}/status.
  • platform-core/modules/system/mainSystem.py — Navigation Nutzung > RAG-Inventar.
  • ui-nyla/src/pages/views/workspace/WorkspaceRagInsightsPage.tsx + .module.csslöschen.
  • ui-nyla/src/types/mandate.tsrag-insights raus.
  • ui-nyla/src/pages/FeatureView.tsx, App.tsx — Route + Mapping raus.
  • ui-nyla/src/components/AddConnectionWizard/AddConnectionWizard.tsx — Connector-Type-Aware Step-Definition; integrierte Microsoft-Admin-Consent + Infomaniak-PAT Steps; Kostenabschätzung + Preferences-Step entfernen.
  • ui-nyla/src/components/UnifiedDataBar/SourcesTab.tsx — vierter Action-Button für ragIndexEnabled (analog zu 🔒/Neutralize) inklusive Vererbungs-Visualisierung (inheritedRagIndexEnabled-Prop, gestrichelt/gedimmt für vererbte Werte).
  • ui-nyla/src/api/connectionApi.tsKnowledgePreferences reduzieren; neue API-Methoden.
  • NEU ui-nyla/src/pages/system/RagInventoryPage.tsx — Drei-Tab-Seite (Meine / Mandant / Plattform).
  • ui-nyla/src/pages/basedata/ConnectionsPage.tsx — Buttons „Admin-Zustimmung" + „Infomaniak" + Modal entfernen; Master-Toggle pro Row + Status-Pill + Stop-Button bei laufendem Bootstrap hinzufügen.
  • NEU ui-nyla/src/components/Header/RagRunningBadge.tsx — globales Header-Badge mit Anzahl laufender Bootstrap-Jobs + Click-through zur RAG-Inventar.

Bekannte Fallstricke

  • Migration neutralizeBeforeEmbed=true → Pro-DataSource: Bei Schema-Update werden alle existierenden DataSources der Connection mit neutralize=true markiert. Falls keine DataSources existieren (was wahrscheinlich ist, weil heute Walker OAuth-global läuft), gehen die Werte verloren — Release-Note nötig.
  • Migration autoSync-Bedeutung: Wenn autoSync heute schon mit anderem Sinn benutzt wird, ist Renaming sicherer als Umdeutung. Phase A startet mit Code-Audit.
  • Bestehende RAG-Daten ohne DataSource-Bezug: Heute haben SharePoint-/Outlook-Chunks connectionId aber keinen DataSource-Link (Walker walked OAuth-global). Migration: bestehende Chunks bleiben drin, neue Indexierung läuft nur über DataSources. „Legacy-Chunks" sichtbar machen oder einmalig purgen — offene Entscheidung.
  • Cancel-Race-Condition: Walker prüft Cancel-Flag alle N Items. Im Worst Case dauert Stop noch N Items lang. Akzeptabel für v1.
  • Daily Resync vs. Cancel: Wenn ein Daily-Resync läuft und User stoppt — startet er morgen neu? Ja, das ist gewollt. Wenn User dauerhaft Pause will → Master-Toggle off oder einzelne DataSource-Toggles off.

Ziel und Nicht-Ziele

Ziel:

  • Single Source of Truth für Neutralisierung: ausschliesslich DataSource.neutralize, gemanagt in der UDB.
  • Per-Element RAG-Index-Toggle in der UDB (analog zu Scope/Neutralize).
  • Vollständiger Lebenszyklus für Wizard-Consent (Erteilen, Widerrufen) im UI sichtbar.
  • Stop-Funktion für laufende Bootstrap-Jobs (Job-Cancel + Walker-Cancel-Check).
  • Globale Start > Nutzung > RAG-Inventar Seite mit drei Tabs (Meine Daten / Mandant / Plattform).
  • Bootstrap-Walker konsumieren DataSource-Liste (nur jene mit ragIndexEnabled = true).
  • WorkspaceRagInsightsPage ist gelöscht.
  • Wizard ist auf Anbieter + Consent reduziert (keine Kostenschätzung, keine Preferences, keine Neutralisierung).
  • Tote Pref-Felder entfernt (mimeAllowlist, surfaceToggles, neutralizeBeforeEmbed).

Explizit NICHT:

  • Wir bauen keinen zweiten Knowledge-Store.
  • Wir verändern nicht die Retrieval-Seite (buildAgentContext).
  • Wir bauen kein Per-Item-Revoke (Granularität: Element-Toggle in UDB oder Master-Connection-Toggle — nicht eine einzelne Mail).
  • Wir bauen keine Auto-Default-DataSources beim Connect — User muss aktiv Indexierung pro Element wählen.
  • Wir bauen keine Echtzeit-Cost-Caps (z.B. „stop bei CHF 5.-") — v1: Stop ist manuell. Cost-Caps sind Roadmap v2.

Betroffene Module

  • Gateway: serviceKnowledge (Walker-Refactor, Purge-Endpoints, Cancel-Check), serviceBackgroundJobs (Cancel-API), routeDataConnections (Knowledge-Felder + PATCH-Endpoints), routeDataSources (PATCH /rag-index), neuer routeRagInventory, mainSystem (Navigation), datamodelUam, datamodelDataSource.
  • Frontend: WorkspaceRagInsightsPage löschen, AddConnectionWizard stark vereinfachen (Steps, keine Kostenschätzung), ConnectionsPage (Master-Toggle + Stop-Button), SourcesTab (4. Index-Toggle-Button), neue RagInventoryPage, pageRegistry.
  • DB-Migration: ja — knowledgePreferences.neutralizeBeforeEmbed/mimeAllowlist/surfaceToggles raus; DataSource.autoSyncragIndexEnabled (oder Re-Doku).
  • Andere Komponenten: keine.

Entscheidungen

Datum Entscheidung Begründung
2026-05-12 Neutralisierung wird ausschliesslich pro DataSource verwaltet Single Source of Truth; UDB ist der einzige Ort für Datenobjekt-Policies
2026-05-12 Index-Aktivierung wird pro Tree-Element via UDB-Toggle gesteuert (analog Scope/Neutralize) Konsistente UX, granular, revoke-trivial
2026-05-12 WorkspaceRagInsightsPage wird gelöscht, ersetzt durch globale Start > Nutzung > RAG-Inventar RAG ist plattformweit, nicht workspace-scoped
2026-05-12 Walker iterieren über DataSource-Rows mit ragIndexEnabled=true der Connection User-kontrollierter Scope; per-Source Revoke trivial
2026-05-12 Keine Auto-Default-DataSources beim OAuth-Connect DSGVO-konformer (kein Default-Indexing); User muss aktiv wählen
2026-05-12 Kostenabschätzung im Wizard wird entfernt Unzuverlässig, erzeugt falsche Sicherheit; Kostenkontrolle via Stop-Button + Per-Element-Opt-In
2026-05-12 Stop-Mechanismus: implizit (Toggle off) + explizit (Cancel-Job-Button) Jederzeit stoppbar, kein „Walker rennt weg"
2026-05-12 „Was ist neu zu indexieren" wird nicht neu erfunden — bestehende Hash-Idempotenz reicht Pure Function über Content; bereits implementiert und getestet
2026-05-12 Surface Toggles und MIME-Allowlist werden aus Schema entfernt (nicht implementiert) Per-Element-Toggle macht sie obsolet
2026-05-12 Vererbungs-Mechanik für Tree-Policies: neutralize und ragIndexEnabled kaskadieren auf Container, lokale Overrides möglich (analog zu inheritedScope/inheritedNeutralize Pattern) Konsistenz mit existierender UDB-Logik; spart redundante Per-File-Toggles
2026-05-12 Tabellen-Granularität: Neutralisierung pro Feld (neutralizeFields[]); RAG-Index pro Tabelle (kein Per-Feld) Bestehende UI-Pattern bleiben; Per-Feld-Index wäre Over-Engineering für v1
2026-05-12 Wizard ist einziger Eintrittspunkt für alle Connector-Typen — Buttons „Admin-Zustimmung" + „Infomaniak" auf ConnectionsPage werden entfernt und in den Wizard integriert Genau ein Pfad pro Connector-Typ; eliminiert Parallelmechanismen
2026-05-12 Drei Sichtbarkeits-Orte für laufende Indexierung: Header-Badge, ConnectionsPage-Row, RagInventoryPage-Tab User darf nirgends „raten" was läuft; Stop-Aktion an mindestens zwei Orten erreichbar

Umsetzungs-Checkliste

Phase A — Datenmodell & Cancel-Infrastruktur (Tage 13)

  • Audit: existierende Verwendung von DataSource.autoSync prüfen → Entscheidung: Renaming ragIndexEnabled vs. Re-Doku.
  • datamodelDataSource.DataSource: ragIndexEnabled: bool = False (default False — kein Auto-Index ohne explizite Wahl). lastIndexed: Optional[float].
  • Migration: script_db_migrate_datasource_rag.py (Rename autoSyncragIndexEnabled, lastSyncedlastIndexed).
  • datamodelUam.UserConnection.knowledgePreferences — Schema-Doku: neutralizeBeforeEmbed, surfaceToggles, mimeAllowlist raus.
  • subConnectorPrefs.ConnectionIngestionPrefs — tote Felder entfernt (nur Mail/ClickUp/File-Prefs bleiben).
  • serviceBackgroundJobs.cancelJob(jobId) -> bool API hinzugefügt (setzt Status auf CANCELLED).
  • JobProgressCallback: isCancelled() -> bool (liest Job-Status aus DB; cached 3s).
  • Walker-Refactor: Alle 5 subConnectorSync*.py Walker iterieren über ragIndexEnabled=true DataSources, Cancel-Check alle 50 Items, provenance.dataSourceId.
  • Purge-Helpers in interfaceDbKnowledge: deleteFileContentIndexByDataSource(dataSourceId).

Phase B — APIs (Tage 35)

  • GET /api/connections/ Antwort um knowledgeIngestionEnabled und knowledgePreferences erweitert.
  • PATCH /api/connections/{id}/knowledge-consent mit Purge + Cancel bei false, Bootstrap bei true.
  • PATCH /api/connections/{id}/knowledge-preferences — speichert Mail-Tiefe etc.
  • POST /api/connections/{id}/knowledge-stop — cancelled alle laufenden Bootstrap-Jobs der Connection.
  • PATCH /api/datasources/{id}/rag-index mit Mini-Bootstrap bei true, Purge bei false.
  • Neue Route routeRagInventory: /me, /mandate, /platform, /jobs, /reindex/{connectionId}.
  • GET /api/rag/connection/{id}/statusEntscheidung: nicht umgesetzt — Status-Infos laufen über Inventory-Aggregation.
  • Workspace-RAG-Endpoint gelöscht: routeFeatureWorkspace.rag-statistics raus.

Phase C — Frontend Cleanup (Tage 56)

  • Gelöscht: WorkspaceRagInsightsPage.tsx + .module.css.
  • mandate.ts: rag-insights Workspace-View raus (2026-05-15).
  • FeatureView.tsx: Guard-Condition für rag-insights entfernt (2026-05-15).
  • App.tsx: Route rag-insights entfernt (2026-05-15).
  • Wizard-Refactor (AddConnectionWizard): Connector-Type-Aware Steps (Google/MSFT/ClickUp/Infomaniak), MSFT Admin-Consent + Infomaniak-PAT integriert, Cost-Logik + Prefs-Step entfernt.
  • ConnectionsPage Cleanup: Admin-Zustimmung + Infomaniak Standalone-Buttons entfernt (nur noch via Wizard).

Phase D — Frontend UDB & RAG-Sichtbarkeit (Tage 69)

  • SourcesTab.tsx: 4. Action-Button (🧠) pro Tree-Node mit Vererbungs-Visualisierung (inheritedRagIndex).
  • RagInventoryPage ist der primäre Ort für Knowledge-Consent-Toggle, Stop-Button und Sync-Status pro Connection. Entscheidung: ConnectionsPage erhält keine duplizierte Master-Toggle/Stop-UI — Nutzer navigieren für RAG-Management zur RagInventoryPage.
  • ConnectionsPage.tsx Master-Toggle + Status-Pill + Stop-Button pro Rowbewusst nicht umgesetzt (Preferences und RAG-Steuerung primär auf RagInventoryPage).
  • KnowledgePreferencesDrawerbewusst nicht umgesetzt (Preferences über RagInventoryPage oder Backend-Defaults).
  • Globales Header-Indikator: RagRunningBadge als Floating-Komponente in MainLayout.tsx, Polling gegen /api/rag/inventory/jobs.

Phase E — Frontend RAG-Inventar-Seite (Tage 811)

  • Neue Seite RagInventoryPage mit Consent-Toggle, Stop-Button, Reindex-Button, DataSource-Übersicht pro Connection.
  • pageRegistry.tsx + App.tsx: Route eingerichtet.
  • mainSystem.py Navigation: Eintrag Nutzung > RAG-Inventar.

Phase F — DSGVO Sichtbarkeit + Tests (Tage 1113)

  • DSGVO-Audit-Log: knowledge_consent_changed, rag_index_toggled, knowledge_jobs_stopped werden via audit_logger geloggt.
  • Backend-Integrationstests: manuell verifiziert (Smoke-Tests, 2026-05-15).
  • Frontend-Tests: manuell verifiziert (2026-05-15).
  • Wiki-Updates: b-reference/platform-core/ai-agent.md aktualisiert; TOPICS.md enthält RAG-Inventar-Eintrag.

Akzeptanzkriterien

# Kriterium (Given-When-Then) Prio
1 Given ich habe Wizard-Consent true, when ich auf der ConnectionsPage den Master-Toggle auf off setze und bestätige, then werden alle FileContentIndex + ContentChunk-Rows der Connection synchron gelöscht, knowledgeIngestionEnabled = false gesetzt, und alle laufenden Bootstrap-Jobs dieser Connection cancelled. must
2 Given in der UDB ist für DataSource X ragIndexEnabled = true und neutralize = true, when der Bootstrap/Resync läuft, then werden Items aus diesem Pfad mit Neutralisierung indexiert; and Items aus anderen DataSources der gleichen Connection werden nur indexiert wenn ihr eigener ragIndexEnabled = true ist. must
3 Given in der UDB klicke ich den Index-Toggle einer DataSource auf off und bestätige, then wird ragIndexEnabled = false gesetzt, ein Purge-Job entfernt alle FileContentIndex-Rows mit matching connectionId + pathPrefix, und Toast zeigt „X Inhalte aus RAG entfernt". must
4 Given ein Bootstrap-Job läuft (status=RUNNING), when ich den Stop-Button drücke, then wird cancelJob aufgerufen, der Walker beendet sich nach max. ~50 Items, Job-Status wird CANCELLED, Result enthält cancelled: true, processedSoFar: N, bereits indexierte Items bleiben drin. must
5 Given ich öffne Start > Nutzung > RAG-Inventar, then sehe ich Tab „Meine Daten" mit allen meinen Connections + DataSources + Item-Counts + letztem Sync + Stop-Button bei laufenden Jobs. And Tab „Mandant" / „Plattform" nur, wenn ich entsprechende Rolle habe. must
6 Given der WorkspaceRagInsightsPage-Code, then existiert er nicht mehr im Repo; rag-insights ist aus mandate.ts, FeatureView.tsx, App.tsx entfernt; /api/workspace/{instanceId}/rag-statistics liefert 404. must
7 Given der AddConnectionWizard, then zeigt er keine Kostenabschätzung, keine Neutralisierungs-Checkbox und keinen Preferences-Step. Nur: Anbieter → Consent → Connect. must
8 Given UserConnection.knowledgePreferences, then existieren die Felder neutralizeBeforeEmbed, surfaceToggles, mimeAllowlist nicht mehr im Schema und sind in keinem Walker referenziert. Migration: bestehende neutralizeBeforeEmbed=true-Werte wurden auf zugehörige DataSources übertragen. must
9 Given ich frisch Microsoft verbinde mit Wizard-Consent true und ich habe noch keine DataSources mit ragIndexEnabled=true angelegt, then läuft kein Bootstrap (Walker findet leere DataSource-Liste, Job endet sofort mit processed: 0). must
10 Given in der UDB sind 3 Tree-Elemente mit ragIndexEnabled=true markiert, when ich auf einer Connection-Row in ConnectionsPage „Knowledge sync starten" drücke (oder der Daily-Resync läuft), then indexiert der Walker exakt diese 3 Pfade, und der dedup-Mechanismus skippt unveränderte Items per Hash. must
11 Given jeder Toggle (Connection-Master, DataSource-Index, DataSource-Neutralize), when er geändert wird, then wird der Wechsel im aiAuditLogger (oder Audit-Log) festgehalten mit userId, timestamp, target, oldValue, newValue. should
12 Given ich öffne den Knowledge-Preferences-Edit-Drawer auf einer Connection-Row, then sehe und editiere ich mailContentDepth, mailIndexAttachments, clickupScope, maxAgeDays (aber nicht neutralizeBeforeEmbed — dieses Feld existiert nicht mehr). should
13 Given ich setze in der UDB neutralize=true auf einem SharePoint-Site-Knoten (Container) und ragIndexEnabled=true auf demselben, when der Walker den Site indexiert, then werden alle Files unter dem Site mit Neutralisierung indexiert; and wenn ein Subfolder eine eigene neutralize=false Policy hat, werden dessen Files ohne Neutralisierung indexiert (Vererbung mit lokaler Override). must
14 Given ein FeatureDataSource für eine Trustee-Tabelle mit neutralizeFields=["email","name"] und ragIndexEnabled=true, when der Walker indexiert, then sind in den persistierten Chunks die Felder email und name neutralisiert, alle anderen Felder im Klartext. must
15 Given auf der ConnectionsPage existieren keine separaten Buttons mehr für „Admin-Zustimmung" und „Infomaniak", when ich „Verbindung hinzufügen" klicke und „Microsoft" wähle, then zeigt der Wizard einen optionalen „Admin-Zustimmung"-Step mit Hinweistext und Skip-Möglichkeit; and wenn ich „Infomaniak" wähle, zeigt er einen PAT-Eingabe-Step. must
16 Given ich starte im Wizard einen Infomaniak-Connect, when ich im PAT-Step abbreche, then wird die im Backend angelegte pending Connection wieder gelöscht (kein Orphan), analog zur heutigen handleInfomaniakCancel-Logik. must
17 Given ein Bootstrap-Job einer Connection läuft, then sehe ich an drei Orten den Status: (a) globales Header-Badge mit Anzahl laufender Jobs, (b) ConnectionsPage-Row mit Status-Pill + Stop-Button, (c) RagInventoryPage Tab „Meine Daten" mit Detail + Stop-Button. Alle drei Orte zeigen dieselben Counter (gleicher Backend-Endpoint). must
18 Given ich klicke das globale Header-Badge, then werde ich zur RagInventoryPage (Tab „Meine Daten") navigiert. should

Testplan

ID AC Art Automatisiert Repo-Pfad Status
T1 1 api+integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T2 2 unit manuell Smoke-Test manuell verifiziert (2026-05-15)
T3 3 api+integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T4 4 unit+integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T5 5 api+ui manuell Smoke-Test manuell verifiziert (2026-05-15)
T6 6 repo+api manuell Smoke-Test manuell verifiziert (2026-05-15)
T7 7 ui manuell Smoke-Test manuell verifiziert (2026-05-15)
T8 8 unit+migration manuell Smoke-Test manuell verifiziert (2026-05-15)
T9 9 integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T10 10 integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T11 11 unit manuell Smoke-Test manuell verifiziert (2026-05-15)
T12 12 ui manuell Smoke-Test (ConnectionsPage RAG-UI bewusst nicht umgesetzt, D-6) manuell verifiziert (2026-05-15)
T13 13 integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T14 14 integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T15 15 ui manuell Smoke-Test manuell verifiziert (2026-05-15)
T16 16 ui+integration manuell Smoke-Test manuell verifiziert (2026-05-15)
T17 17 ui+e2e manuell Smoke-Test manuell verifiziert (2026-05-15)
T18 18 ui manuell Smoke-Test manuell verifiziert (2026-05-15)
  • Vorgänger-Plan: wiki/c-work/4-done/2026-04-id-unified-knowledge-indexing-rag-concept.md
  • Architektonisch betroffen: wiki/b-reference/platform/neutralization.md, wiki/b-reference/platform-core/ai-agent.md, wiki/b-reference/ui-nyla/architecture.md
  • Code-Touchpoints: siehe Abschnitt „Kritische Stellen im Code"

Abschluss

  • b-reference/platform-core/ai-agent.md aktualisiert (Knowledge Lifecycle, DataSource-getriebener Walker, Cancel-Mechanismus)
  • b-reference/platform/neutralization.md aktualisiert (DataSource = SSoT, kein neutralizeBeforeEmbed mehr, Tree-Vererbung via subPolicyResolver) — 2026-05-15
  • b-reference/ui-nyla/architecture.md aktualisiert (RagInventoryPage, UDB 4. Action-Button, RagRunningBadge, AddConnectionWizard) — 2026-05-15
  • TOPICS.md aktualisiert (RAG-Inventar als neuer Kanon-Eintrag)
  • Dieses Dokument in c-work/4-done/ (Status → done, 2026-05-15)
  • Eintrag im c-work/_CHANGELOG.md