# Changelog (c-work) Eine Zeile pro Change, NEUESTE EINTRAEGE ZUOBERST. Begruendungen gehoeren ins zugehoerige `c-work//.md` oder die PR-Beschreibung. Format: `- YYYY-MM-DD | | | [(c-work: )] [(PR: #123)]` type: `feat` `fix` `refactor` `docs` `test` `chore` `build` · scope: `platform-core` `ui-nyla` `service-llm-private` `service-preprocessing` `teams-bot` `wiki` `infra` `*` Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler. ## 2026-06-08 - 2026-06-08 | refactor | platform-core | **ServiceHub eliminiert + ServicesBag als einziger Service-Bag**: `serviceCenter/serviceHub.py` (190 LOC, `ServiceHub`-Klasse mit 3 blanken Interface-Properties + 7 dynamischen Feature-Interfaces) komplett geloescht. 5 Consumer (`neutralizePlayground`, `serviceBzo`, `serviceAiIntent`, `routeSharepoint`, `routeClickup`) + 5 Test/Eval-Dateien auf `getService()` + `ServiceCenterContext` migriert. `_WorkflowAutomationServiceHub` eliminiert, `_ServicesAdapter` zu `ServicesBag` umbenannt — es gibt jetzt genau EINEN Service-Bag im gesamten System. Cursor Rule `service-architecture.mdc` erstellt zur Durchsetzung. Wiki `architecture.md` aktualisiert. - 2026-06-08 | refactor | platform-core | **Blanko Interface-Lecks gekappselt (B5)**: 14 neue ChatService-Methoden (File/Folder CRUD, Workflow-Listing) als Kapselungsschicht. ~60 Caller in coreTools, Workflows, AI/Generation/Neutralization von `interfaceDbComponent.*`/`interfaceDbChat.*` auf `chatService.*` migriert. `ServicesBag.interfaceDbComponent` Property entfernt. 3x toter `saveGeneratedFile`-Code (hasattr-Guard) entfernt. 3x Neutralization-DI aufgeloest (NeutralizationService nutzt jetzt `_getService("chat")`). - 2026-06-08 | refactor | platform-core | **Services-Bag Workaround-Bereinigung (B4)**: Defensive `getattr`-Fallbacks fuer garantierte Attribute (`user`, `mandateId`, `chat`, `extraction`) entfernt. Try/except Interface-Imports durch `services.*` ersetzt. `canAccessService()` auf ServicesBag hinzugefuegt. Bidirektionaler Import `serviceCenter <-> workflows` aufgeloest via `self.services.getService("web")`. - 2026-06-08 | refactor | platform-core | **snake_case -> camelCase Public API (B6)**: `can_access_service` -> `canAccessService`, `clear_cache` -> `clearCache`, `get_resolution_cache` -> `getResolutionCache`, `create_checkout_session` -> `createCheckoutSession`. Keine Legacy-Aliase. - 2026-06-08 | refactor | platform-core | **Deprecated Items bereinigt (B3)**: `GRAPHICAL_EDITOR_DATABASE` -> `WORKFLOW_AUTOMATION_DATABASE` (8 Dateien). `graphical_editor_runs` -> `workflow_automation_runs` + camelCase (runFileLogger + 2 Caller). Demo-Configs: Dead Code (`_ensurePilotWorkflow`) geloescht, `geDb` -> `waDb`, Descriptions aktualisiert. `Automation2WorkflowView` -> `AutoWorkflowView`. - 2026-06-08 | refactor | ui-nyla | **Automation2 UI-Naming eliminiert**: 29 Dateien, 3 File-Renames (`Automation2FlowEditor` -> `WorkflowFlowEditor`, `Automation2FlowEditor.module.css` -> `WorkflowFlowEditor.module.css`, `Automation2DataFlowContext` -> `WorkflowDataFlowContext`). API-Typen umbenannt (`Automation2Graph` -> `WorkflowGraph`, `Automation2Run` -> `WorkflowRun`, etc.). 0 verbleibende `automation2`-Referenzen. - 2026-06-08 | refactor | ui-nyla | **workflowApi.ts Shim aufgeloest**: 23 Consumer auf `workflowAutomationApi` migriert. ClickUp-Funktionen nach `clickupApi.ts` extrahiert. Shim geloescht. FlowEditor `index.ts` Re-Export-Shim geloescht. `connectionPath.test.ts` (stale) geloescht. `tsc-errors.txt`/`i18n_missing_report.md` geloescht + `.gitignore`. - 2026-06-08 | refactor | ui-nyla | **Keep-Alive Fix**: `matchLocation` mit `location.search` Parameter eingefuehrt. Workflow-Automation Keep-Alive matched jetzt nur bei `tab=editor` statt auf allen Sub-Pfaden. - 2026-06-08 | refactor | platform-core | **Import-Violations komplett geloest (Phase 1-3)**: `nodeCatalog/` (L2) extrahiert (Phase 1). External-Tool-Registry invertiert (Phase 2). `systemComponentRegistry` fuer Lifecycle-Hooks (Phase 3). `workflowArtifactVisibility` + `entryPoints` nach shared/nodeCatalog. Ergebnis: `interfaces -> WA = 0`, `system -> WA = 0`, `serviceCenter -> WA = 0`, bidirektional = 0. Alle 11 Aufwaerts-Violations und 2 bidirektionale Kopplungen geloest. - 2026-06-08 | docs | wiki | **architecture.md aktualisiert**: ServicesBag-Sektion mit Architektur-Regeln und verbotenen Patterns. Layer-Hierarchie mit L5b (workflowAutomation). Veraltete Pfade (`serviceHub/`, `automation2/`, `GRAPHICAL_EDITOR_DATABASE`) korrigiert. Typed Action Architecture Pfade auf `workflowAutomation/engine/` und `nodeCatalog/` aktualisiert. ## 2026-06-07 - 2026-06-07 | feat | platform-core, ui-nyla | **Phasen 1–4 ausgeführt (WorkflowAutomation-Plan)**: **Phase 1**: `AutoWorkflow.runAsPrincipal` (Optional), Nav-Block `workflowAutomation` (5 Items), mandatsweite API `routeWorkflowAutomation.py` (10 Endpunkte, `_validateWorkflowAccess` RBAC-Helper), Scheduler `if-not-instanceId`-Guard entfernt, `_validateFeatureInstanceMandates()` Guard in `executionEngine.py`. **Phase 2**: `WorkflowAutomationPage.tsx` Hub-Seite mit Tabs (Workflows/Läufe/Tasks), Route in `App.tsx`, Icons in `pageRegistry.tsx`, `MandateNavigation.tsx` rendert neue Nav-Sektionen als eigene Gruppen. **Phase 3**: `featureInstanceId` → `Optional[str]` (softFk). **Phase 4**: Route-Imports auf kanonische `datamodels.datamodelWorkflowAutomation` umgestellt (`routeWorkflowDashboard`, `routeAutomationWorkspace`, `routeSystem`); GE-Hardcode als DEPRECATED markiert. (c-work: c-work/1-plan/2026-06-automation-system-component.md) - 2026-06-07 | feat | platform-core | **Phase 1 teilweise ausgeführt (WorkflowAutomation-Plan)**: `AutoWorkflow.runAsPrincipal` (Optional, nullable, softFk→UserInDB) in `datamodelWorkflowAutomation.py`. Nav-Block `workflowAutomation` (order 25, 5 Items) in `datamodelNavigation.py`. Neue mandatsweite API `routeWorkflowAutomation.py` mit 10 Endpunkten unter `/api/workflow-automation/` + Write-RBAC-Helper `_validateWorkflowAccess` (member=read, admin=write, isPlatformAdmin bypass). Scheduler `if-not-instanceId`-Guard in `mainScheduler.py` entfernt. Runtime-Mandatsvalidierung `_validateFeatureInstanceMandates()` in `executionEngine.py` (defence-in-depth, A0.2). (c-work: c-work/1-plan/2026-06-automation-system-component.md) - 2026-06-07 | refactor | platform-core | **Phase 0 + 0.5 ausgeführt (WorkflowAutomation-Plan)**: Delaminierung erledigt — `PauseForHumanTaskError`/`PauseForEmailWaitError` → `datamodels/serviceExceptions.py`, `coerceDocumentDataToBytes` → `shared/documentUtils.py`, Re-Export-Shims in `automation2/executors/`, Cross-Boundary-Caller umgebogen. Scheduler-Boot von `mainGraphicalEditor.onStart` in System-Lifespan (`app.py`, nach `eventManager.start()`) verschoben; `onStart`/`onStop` aus `mainGraphicalEditor.py` entfernt; Scheduler- und Email-Poller-Stop in `app.py` Shutdown (Schritt 3.5). Guard-Test bestätigt: `methods/` lädt kein `automation2` mehr transitiv. (c-work: c-work/1-plan/2026-06-automation-system-component.md) - 2026-06-07 | docs | wiki | **WorkflowAutomation-Plan an Import-Refactoring (2026-06-05/06) nachgeführt**: Codebase-Abgleich gegen `local/notes/refernce-analysis/platform-core-import-analyse.md`. Neue **Layer-Hierarchie L0–L7**; viele Plan-Vorarbeiten **bereits erledigt**: Contracts/Models nach `datamodels` (L1, `datamodelPortTypes`/`datamodelWorkflowAutomation`, statt geplantem `workflowContracts/`), Feature-Lifecycle-Hooks (`mainGraphicalEditor.onInstanceCreate/onMandateDelete/onBootstrap`, ex interfaceFeatures/interfaceDbApp/interfaceBootstrap), `NAVIGATION_SECTIONS`→`datamodels/datamodelNavigation`, `EventManager`/`parseInlineRuns`→shared, `serviceHub`→serviceCenter. GE↔Engine-Coupling wurde im Refactoring **explizit auf diesen Plan vertagt** (Deferred-Liste übernommen): `features.graphicalEditor↔workflows` (21/40), `serviceCenter→GE` (5× workflowTools), `interfaceDbManagement:936`, Re-Export-Shims. **Boundary-Leck besteht weiter**: `PauseForHumanTaskError`/`coerceDocumentDataToBytes` noch in `automation2/executors`, von `methods/` importiert → Delaminierung (→ `datamodels/serviceExceptions.py` bzw. `shared/documentUtils.py`) bleibt erster Schritt. Neuer Abschnitt «Code-Stand», Phase 0.5/Betroffene Module/Entscheidungen/Links angepasst. (c-work: c-work/1-plan/2026-06-automation-system-component.md) ## 2026-06-05 - 2026-06-05 | docs | wiki | **WorkflowAutomation-Plan nach 2 kritischen Code-Reviews überarbeitet (+ Trennung Automation ↔ Execution)**: Zentrale Korrektur — **Automation** (Graph-Editor/Engine `automation2`/Scheduler/Solutions) und der geteilte **Workflow-Execution-Layer** (`workflows/methods`+`processing`, von reinen Chats/Agents genutzt) bleiben im Code getrennt; nur die Automation-Teile wandern in die Komponente. Frühere Aussage «`automation2` wird von Chats nicht importiert» als **falsch** korrigiert (shared `methods` importieren `automation2.executors`) → neue **Phase 0.5 Delaminierung** (`PauseForHumanTaskError`/`_coerce_document_data_to_bytes` + `portTypes`/`STATIC_NODE_TYPES` → neues geteiltes `workflows/workflowContracts/`). Weitere Review-Fixes: `featureInstanceId` ist **bereits DB-nullable** (kein DDL, keine DB-FK), Scheduler ist hart auf `featureInstanceId` gekeyt (Entkopplung als eigener Schritt), Dashboard-RBAC deckt nur Lesen → eigener Write-Helper `_validateWorkflowAccess` für 39 Sites, RBAC-Namespace **Option B** (kein Flip), Template-Instanziierungs-Trigger (`_copyTemplateWorkflows`) wandert in `launcher/`, per-Node-Billing als bewusste Executor-Ausnahme, `.cursor/rules/*` aktualisieren. (c-work: c-work/1-plan/2026-06-automation-system-component.md) - 2026-06-05 | docs | wiki | **Plan: System-Komponente `WorkflowAutomation` (`graphicalEditor` raus aus dem Feature-Modell)**: Neuer Umsetzungsplan in `c-work/1-plan/`. Komponente heisst `WorkflowAutomation` (greppbares Token; `graphicalEditor` = ein Modul darin), modulares Layout (toolbox/editor/engine/scheduler/solutions/launcher/monitoring) gemappt auf L1–L4. Code-fundiert (Discovery/Registry, `FeatureInstance`-Lifecycle, DB `poweron_graphicaleditor`, Scheduler-Boot via Feature-`onStart`, RBAC-Präzedenz `routeWorkflowDashboard`/`routeAutomationWorkspace`, ~70 Backend-Touchpoints + UI-Navigation). Phasen 0–4: Scheduler-Boot in System-Lifespan → mandatsweite API `/api/workflow-automation/…` + System-RBAC → Top-Level-Nav-Gruppe «Workflow-Automation» mit Tab-Seiten → DB (`runAsPrincipal`, `featureInstanceId` als RBAC-Anker abbauen, Daten-Backfill) → Feature-Mantel entfernen. Vorgezogen aus A0.4, weil Voraussetzung für die Solution-Schicht. (c-work: c-work/1-plan/2026-06-automation-system-component.md) - 2026-06-05 | docs | wiki | **CustomerCases A0.4 von Roadmap auf «jetzt» gezogen**: step1-architecture + step3-features-plan verweisen für die `graphicalEditor`→System-Komponente-Umstellung auf den neuen 1-plan; offene Frage #8 + Checkliste/Entscheidungstabelle entsprechend aktualisiert. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md) - 2026-06-05 | docs | wiki | **A0 Code-verifiziert korrigiert: Workflow-Ownership = Mandant + Run-as-Principal (nicht Host-Feature)**: Code-Analyse (`executionEngine.py`, `mainScheduler.py`, `routeFeatureGraphicalEditor.py`, `datamodelFeatureGraphicalEditor.py`, Trustee-Actions) zeigt, dass Workflows mandatsweit + identitätsgebunden über beliebige Feature-Instanzen laufen — kein Host-Feature-Owner. **A0.1** neu: Owner-Achse = `mandateId` + `runAsPrincipal`, RBAC = Principal-RBAC; scheduled Runs sollen unter definierter Automations-Identität statt globalem `event`-Sysadmin laufen. **A0.2** neu: Multi-Feature/Multi-Instanz ist nativ (per-Node `FeatureInstanceRef`), echte Schranke = Mandant; 3 Code-Lücken benannt (Runtime-Mandatscheck beim Ref-Auflösen, Read-RBAC analog Write, per-Node-Billing statt fix `graphicalEditor`). **A0.4** neu: `graphicalEditor` als Orchestrierungs-Substrat eingeordnet (kein DATA, eigene DB für alle Features, globaler Scheduler-Boot via `onStart`) — Feature-Mantel bleibt als RBAC-/Store-Eintritt. step1-architecture + product-summary angeglichen. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md) - 2026-06-05 | docs | wiki | **CustomerCases step3-Pläne überarbeitet + 3 Architektur-Leitentscheide (A0)**: Nach kritischem Review beide Pläne geschärft. Neuer Abschnitt **A0** im features-plan löst die 3 kritischen Punkte: **A0.1** Solution = `graphicalEditor`-Execution mit `hostFeatureRef` als alleinigem Owner für RBAC/Billing (Cross-Feature-Drift gelöst); **A0.2** Multi-Instanz-Fan-out nur mandatsintern über explizites `instanceSet`, instanz-genaue Billing-Attribution; **A0.3** Settings als Run-Envelope-Injektion (`trigger.form`/DataRef) statt Graph-Mutation (Published-Version-Invariante gewahrt). Zusätzlich: Node-IDs gegen `nodeDefinitions/` verifiziert (neuer `email.sendEmail`-Versand-Node + `data.writeToTable` als Blocker erkannt; `data.consolidate` vorhanden), neuer i18n-Abschnitt (A5, `t()` + `TextMultilingual`), RBAC-Items/Deletion-Cascade/Adapter-Drift ergänzt, S5 als «vorhandene Templates exponieren» (statt migrieren) korrigiert. step1-architecture + product-summary + mockup auf neue Entscheide/Node-IDs angeglichen. (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-features-plan.md, 2026-06-CustomerCases-step3-solutions-plan.md) ## 2026-06-04 - 2026-06-04 | docs | wiki | **CustomerCases-Doks auf einheitliche Naming-Convention umbenannt**: `2026-06-STEP*`-Dateien → `2026-06-CustomerCases-stepN-*` (step1=architecture, step2=communication [product-summary md/pdf + mockup html], step3=solutions-plan/features-plan). Alle Cross-Referenzen in den Dokumenten + Changelog-Pfade angepasst. (c-work: c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md u. a.) - 2026-06-04 | docs | wiki | **STEP2-Pläne neu strukturiert: Solutions vs. Features**: Die drei bisherigen STEP2-Docs (lawyer-feature, pm-consolidated-customer-requirements, umsystem-integration) durch zwei architektur-konforme Pläne ersetzt. `step3-solutions-plan.md` = alle konfigurierten Use Cases als Solutions (S1 SelectLine→RMA, S2 Pling-Reporting, S3 PWG-Mietzins, S4 Beleg-Import, S5 Finanz-Analysen, S6 Notification) mit Trigger/Bausteine/Settings/Output je Solution + Klassifikationstabelle. `step3-features-plan.md` = der Code dahinter (Teil A Plattform-Enabler: Solution-Schicht L3/L4, generische Nodes rbac.queryUsersByRole/testMode/integration.*, Connectors; Teil B vertikale Features: lawyer, commcoach, trustee-core). (c-work: c-work/0-ideas/2026-06-CustomerCases-step3-solutions-plan.md, 2026-06-CustomerCases-step3-features-plan.md) - 2026-06-04 | docs | wiki | **Konzept: Solution-Schicht (Customer Workflows) + Pling-Use-Case**: Architektur-Idee fuer generische L3/L4-Solution-Schicht ueber dem Workflow-Stack (Use Case → konfigurierter Workflow, kein Kunden-Code). Pling «Kaffee-Klatsch» Monatsreporting als staerkstes Validierungsbeispiel eingearbeitet (Multi-Instanz-Fan-out, rollenbasierte Verteilung). Plus generische Product-Summary und klickbares HTML-Mockup im ui-nyla-Look. (c-work: c-work/0-ideas/2026-06-CustomerCases-step1-architecture.md, 2026-06-CustomerCases-step2-communication-product-summary.md, 2026-06-CustomerCases-step2-communication-mockup.html) - 2026-06-04 | docs | wiki | **Plan: Umsystem-Integration (Connectors + Workflows + Datenquellen-Seiten)**: Arbeitsdokument fuer generische Umsystem-Anbindung (SelectLine→RMA als Pilot). Trennung generische Bausteine (Connectors/kanonische Modelle/Workflow-Nodes) vs. kundenspezifische Logik (Workflow-Graph + Config). UI-Muster «Datenquelle = Seite» pro Kategorie (Buchhaltung existiert, Saläre als Idee). (c-work: c-work/1-plan/2026-06-umsystem-integration-connectors-und-datenquellen-seiten.md) ## 2026-06-03 - 2026-06-03 | fix | platform-core | **Image MIME-Type Pre-Validation in Extraction Pipeline**: Unsupported Bild-Formate (image/bmp, image/tiff, image/x-icon etc.) werden jetzt VOR dem Failover-Loop erkannt und uebersprungen. Magic-Bytes-Sniffing korrigiert falsche MIME-Types. Verhindert 6000+ Error-Storm bei EML-Verarbeitung (vorher: jedes unsupported Bild x 10+ Models = 30+ API-Errors). Safety-Net auch in aicorePluginAnthropic. - 2026-06-03 | fix | platform-core | **App Shutdown: ResilientHttp Sessions + EventManager Tasks**: `closeAllResilientHttp()` schliesst alle shared aiohttp-Sessions beim Shutdown. `EventManager.shutdown()` cancelt pending Cleanup-Tasks. Behebt 30s+ Shutdown-Hang. - 2026-06-03 | fix | ui-nyla | **Data-Source Chip UUID-Fix**: Race Condition in WorkspaceInput behoben -- `refreshDataSources()` gibt jetzt Promise zurueck, `_handleDataSourceDrop` wartet darauf bevor `pendingAttachDsId` gesetzt wird. Lokaler Label-Cache als Fallback. Verhindert UUID-Anzeige in Chips. - 2026-06-03 | fix | platform-core | **Model Selector: totalTokens statt promptTokens**: Filter in `aicoreModelSelector.py` prueft jetzt `totalTokens` (prompt+context) statt nur `promptTokens` gegen 80%-Limit. Verhindert Auswahl von Modellen mit zu kleinem Context-Window bei grossen Dokumenten. - 2026-06-03 | fix | platform-core | **simpleMode an Chunking-Pipeline angebunden**: `process.py` simpleMode nutzt jetzt `_resolve_file_refs_to_content_parts` / `_action_docs_to_content_parts` statt roher Bytes-Konkatenation. Dokumente fliessen durch die existierende Extraction+Chunking-Pipeline, oversized Prompts (4M+ Tokens) werden vermieden. - 2026-06-03 | fix | platform-core | **Image-Element-Normalisierung**: `_normalizeImageElement` in `subStructureFilling.py` stellt sicher, dass AI-generierte Image-Elemente immer das `content`-Dict-Format haben. Behebt Crash in rendererMarkdown wenn AI flache Keys liefert. - 2026-06-03 | fix | platform-core | **Pre-Flight Guard in callWithTextContext**: Defense-in-depth in `interfaceAiObjects.py` -- lehnt Payloads ab die >95% des besten Modell-Context-Windows ueberschreiten, bevor die Failover-Schleife startet. - 2026-06-03 | fix | platform-core | **PPTX Tabellen-Positionierung**: `_renderSlideContentWithFrames` in rendererPptx.py umgebaut auf sequenzielles Y-Tracking. Textboxen werden mit geschaetzter Hoehe statt full-height erstellt, Tabellen und Code-Blocks erhalten explizite `top`-Positionen. Behebt Bug wo Tabellen unterhalb des sichtbaren Slide-Bereichs erschienen. - 2026-06-03 | feat | platform-core | **HTTP Resilience Mixin fuer Connectors**: Neues `_httpResilience.py` mit `ResilientHttp`-Klasse (Shared aiohttp.ClientSession, asyncio.Semaphore, Retry mit Backoff bei 429/502/503/504, Retry-After Header). Integriert in Google, MSFT und Infomaniak Connectors. Eliminiert 429-Storms durch Concurrency-Limit (Google 8, MSFT 10, Infomaniak 6). - 2026-06-03 | feat | platform-core | **Sandbox readFileBytes + SafeZipFile**: `sandboxExecutor.py` erweitert um `readFileBytes(fileId)` Builtin (rohe Bytes, 50MB-Limit) und `SafeZipFile` Wrapper (read-only, in-memory, kein extract/write). Ermoeglicht ZIP/XLSX/DOCX-Strukturanalyse im Agent-Sandbox ohne Dateisystem-Zugriff. - 2026-06-03 | refactor | platform-core | **Style-System Overhaul: THEME_PRESETS + documentTheme entfernt, AI-Enhancement aktiviert**: `THEME_PRESETS`, `resolveTheme()` und `documentTheme`-Parameter end-to-end entfernt (styleDefaults, mainServiceGeneration, _mediaTools, methodAi, generateDocument, documentPath, mainServiceAi, mainTrustee). Statische Presets ersetzt durch AI-getriebene Style-Enhancement (`_enhanceStyleWithAi` in mainServiceGeneration); DEFAULT_STYLE auf Cursor/VS-Code-Aesthetik modernisiert (Calibri, GitHub-Farben, subtile Rahmen). Dead Code aus allen 5 Renderern entfernt (_getStyleSet, _enhanceStylesWithAI, _getDefaultStyleSet, _validateStylesContrast etc.). (c-work: c-work/4-done/2026-06-style-system-overhaul-smart-tables.md) - 2026-06-03 | fix | platform-core | **Style-Chain-Luecken geschlossen**: AI-Enhancement-Prompt bekommt jetzt das vollstaendige Style-Set (~920 Bytes) statt nur fonts/colors/table (3 von 12 Sektionen). `_convertUnifiedStyleToInternal` erweitert um page, image, table font sizes, borderWidth, codeBlock.borderColor, coverPage, caption, table_banding, table_padding, fonts, colors. Fallback-Farben an DEFAULT_STYLE angeglichen (#1F3864->#24292e, lineSpacing 1.15->1.5). Heading-Key-Mismatch in XLSX/PPTX behoben (heading->heading{level}); DOCX h3/h4 Styles registriert. - 2026-06-03 | feat | platform-core | **Smart Table Styling**: Per-Table Style-Overrides via `content.tableStyle` im JSON-Block (deep-merge mit globalem Table-Style). Neue Style-Keys: `borderStyle` (grid/horizontal/none), `bandingEnabled` (Toggle), `cellPaddingPt`. Automatische Spaltenausrichtung (`_inferColumnAlignments`: Zahlen rechts, Daten zentriert, Text links) oder explizit via `columnAlignments`. Neue Schema-Sektionen `coverPage` und `caption` in DEFAULT_STYLE. Alle 5 Renderer (PDF/DOCX/PPTX/XLSX/HTML) implementieren borderStyle-Varianten, banding-Toggle und columnAlignments. - 2026-06-03 | refactor | platform-core | **HTML-Renderer Legacy-Pfad entfernt**: `_getStyleSet`, `_getDefaultStyleSet` und zugehoerige Legacy-AI-Styling-Logik aus rendererHtml.py entfernt. Nur noch der Unified-Style-Pfad ueber `_convertUnifiedStyleToInternal` + `_generateCssFromUnifiedStyle` ist aktiv. - 2026-06-03 | docs | wiki | **Kanonische Referenzseite Dokumenten-Rendering**: Neue `b-reference/platform-core/document-rendering.md` dokumentiert die vollstaendige Style-Resolution-Pipeline (DEFAULT_STYLE -> Agent-Overrides -> AI-Enhancement -> Per-Element-Overrides), Table-Rendering-Modell, Renderer-Architektur, Font-Resolution, AI-Style-Enhancement-Prompt. TOPICS.md ergaenzt. ADR- und PO-Cleanup-Docs nachgefuehrt. - 2026-06-03 | refactor | * | **Scope von DataSource entfernt (Datenschutz)**: Scope-Flag, -Icon und -Funktionalitaet komplett aus der UDB (Sources-Tab) entfernt — persoenliche Datenquellen duerfen nicht gescoped werden. DB-Spalte `DataSource.scope` bleibt als deprecated, wird nicht mehr gelesen/geschrieben. UDB-Backend (`udbNodes.py`, `_inheritFlags.py`, `routeUdb.py`), Frontend (`UdbSourcesProvider.tsx`), Ingest (`subConnectorIngestConsumer.py`), Auth (`routeDataSources.py`) bereinigt. Scope existiert nur noch bei Files (folder-files). Wiki (`unified-data-bar.md`) aktualisiert. Tests angepasst. ## 2026-06-02 - 2026-06-02 | fix | service-llm-private | **Private-LLM-Pricing byte-basiert statt Pauschale**: `aicorePluginPrivateLlm` rechnete Text/Vision/Reasoning pauschal pro Call ab (CHF 0.01/0.10/0.05, `costPer1kTokens=0`, Bytes ignoriert) -> faktisch fast gratis. Neu byte-/tokenbasiert via `_calcPrivatePriceCHF` mit `PRICE_INPUT_PER_1K=0.0075` / `PRICE_OUTPUT_PER_1K=0.0375`; `costPer1kTokens` der bepreisten Modelle entsprechend gesetzt. Embedding via `_calcPrivateEmbedPriceCHF` (`PRICE_EMBED_PER_1K=0.0005`, nur Input) statt gratis. Hintergrund: Depoformance-Angebot (privates CH-Modell muss teurer sein als oeffentliche Modelle). - 2026-06-02 | feat | platform-core | **Grossdokument: Lazy File-Ref-Bilder (A3/AC15, Schritt 1)**: Block-Bilder koennen jetzt nur eine `fileId` tragen; `renderReport(imageResolver=fileId->bytes)` setzt einen Per-Render-Resolver am Renderer (`BaseRenderer._lazyResolveImageBase64`), den PDF/DOCX `_renderJsonImage` on-demand nutzen. `renderDocument`-Tool haelt fuer pdf/docx Bilder als Referenz (kein base64 mehr im JSON) und uebergibt den Resolver; HTML/Inline/uebrige Formate unveraendert (rueckwaertskompatibel). DOCX bekommt damit echten Ein-Bild-Speicher (python-docx kopiert Bytes sofort ins Package); PDF vermeidet base64-Overhead/JSON-Retention (voll-streamendes PDF via Temp-File-Bilder bleibt als Schritt 2). Tests: tests/serviceGeneration/test_large_document_render.py. (c-work: c-work/1-plan/2026-06-po-cleanup-neutralization-docgen.md) - 2026-06-02 | feat | platform-core | **documentTheme end-to-end + Preset-Bibliothek (A3)**: `styleDefaults.py` bekommt `THEME_PRESETS` (general/finance/legal/technical/hr/marketing) + `resolveTheme`; `resolveStyle(style, documentTheme)` mergt `DEFAULT_STYLE <- Preset <- explizites style` (explizit gewinnt). `documentTheme` ist jetzt durchverkabelt: `ai.generateDocument` Action -> `callAiContent` -> `_handleDocumentGeneration` -> `documentPath.generateDocument` -> `renderResult` -> `renderReport(documentTheme=...)`; ausserdem im `renderDocument`-Tool (neues `documentTheme`-Schema-Property) und in `methodAi.py` (Param an `ai.generateDocument`, Optionen um `marketing` ergaenzt). Reconciliation mit ADR 2026-04 (Presets als agent-ueberschreibbare Defaults, kein Hard-Mandate). Tests in tests/serviceGeneration/test_style_resolver.py. **Offen:** Grossdokument-Pfad (File-Ref-Bilder + Chunked-Render, AC15) — Entscheidung/Scope siehe Plan. (c-work: c-work/1-plan/2026-06-po-cleanup-neutralization-docgen.md) - 2026-06-02 | feat | platform-core | **Typ-/vererbungsbewusste Feld-Neutralisierung (A2)**: `FeatureDataProvider` neutralisiert Quelldaten jetzt regelbasiert via `finalizeRowsAsync`/`_neutralizeAndSerializeRows`: Strings substring-neutralisiert wenn effektiv (explizit ODER geerbt, Feldname als Typ-Hint, async+dedupe ueber die lokale Engine), Binary gedroppt, andere Skalare nur bei explizitem Feld-Flag; IDs/System-Spalten ausgenommen, Engine-Ausfall = [REDACTED]. Policy aus `_featureSubAgentTools` via `resolveEffectiveForFds`; RAG-Bootstrap (`subFeatureBootstrap`) nutzt dieselbe Policy (Query-/Index-Paritaet). Unit-Tests in tests/unit/serviceAgent/test_field_neutralization.py (c-work: c-work/1-plan/2026-06-po-cleanup-neutralization-docgen.md) - 2026-06-02 | feat | * | **Sichere De-Neutralisierung fuer Download (revealDocument)**: Neuer read-only Agent-Tool `revealDocument` loest Platzhalter [typ.uuid] ausschliesslich ueber das private lokale Mapping (resolveText, kein externes LLM) auf und liefert den Klartext als transienten Einmal-Download via SSE-SideEvent `revealDownload`; Klartext wird NICHT gespeichert, indexiert oder in der Chat-Historie abgelegt (persistiertes ToolResult enthaelt nur eine Bestaetigung). Toter `_rehydrateResponse` (nie aufgerufen) aus serviceAi entfernt und callAi/callAiStream-Docstrings korrigiert (keine Rueck-Hydration in Speicher). UI: sseClient + useWorkspace onRevealDownload (base64->Blob->Download->revoke) (c-work: c-work/1-plan/2026-06-po-cleanup-neutralization-docgen.md) - 2026-06-02 | refactor | * | **Chatbot-Feature komplett entfernt**: Backend (features/chatbot, methodChatbot, Routen, Interfaces, RBAC-Namespaces, datamodelChat WORKFLOW_CHATBOT, DB-/Script-Referenzen) und UI (chatbotApi, useChatbot, views/chatbot, ChatbotConfigSection, Routing, FeatureView, pageRegistry, Stores/Hooks/DEBUG-Logs) entfernt (c-work: c-work/1-plan/2026-06-po-cleanup-neutralization-docgen.md) - 2026-06-02 | refactor | platform-core | **LangChain/LangGraph entfernt**: ChatStreamingHelper (langchain_core) geloescht; BZO-Extraktion (realEstate) von LangGraph-StateGraph auf direkte sequentielle Funktionsaufrufe migriert (bzoExtractionLangGraph.py -> bzoExtraction.py); langchain/langchain-core/langgraph/langchain-tavily/nest-asyncio aus requirements.txt entfernt (requirements.lock muss via pip-compile neu generiert werden) - 2026-06-02 | docs | wiki | **Chatbot/LangGraph aus aktiver Doku entfernt**: README, product.md, b-reference (architecture/workflow/diagrams/database), e-compliance/security-overview, a-strategy/roadmap nachgefuehrt (z-archive unveraendert) - 2026-06-02 | feat | platform-core | **Agent data-source search-first guidance**: Sharpened searchDataSource/browseDataSource tool descriptions (per-service query syntax, anti mass-download) and added a search-first rule to buildDataSourceContext so the agent queries server-side instead of downloading whole sources - 2026-06-02 | fix | platform-core | **SharePoint/OneDrive/Drive search scoping + pagination**: SharePoint and OneDrive search now scope to the attached folder and follow @odata.nextLink; OneDrive browse paginates; Google Drive search uses fullText contains (name+content) with pageToken pagination and URL-encoded queries - 2026-06-02 | feat | platform-core | **Outlook mail metadata + date filter**: browse/search expose sender+date inline in the agent tool, browse supports a date-range filter via Graph $filter, and search scopes to the attached folder - 2026-06-02 | fix | platform-core | **Gmail search metadata + label scoping**: search returns real Subject/From/Date (was 'Message {id}'), scopes to the attached label, and resolves message metadata concurrently to cut the sequential N+1 - 2026-06-02 | feat | platform-core | **Google Calendar date-range fetch**: browse/search use timeMin/timeMax when a date range is given, avoiding full multi-year event listings - 2026-06-02 | feat | platform-core | **Contacts metadata inline (MSFT/Google/Infomaniak)**: agent tool shows email/phone/company per contact so vCards are only downloaded when the full record is needed - 2026-06-02 | feat | platform-core | **ClickUp task metadata inline**: agent tool shows status/assignee/due-date per task instead of requiring a JSON download - 2026-06-02 | feat | platform-core | **Infomaniak Calendar date filter**: events honour an agent-supplied date range (clamped to the vendor <3 month limit) instead of a fixed 90-day window ## 2026-05-31 - 2026-05-31 | fix | ui-nyla | **UDB Sources panel compact**: Reduced tree indent from 24px to 16px, shrunk compact row height to 28px, smaller chevrons/icons/action buttons in compact mode for better label visibility - 2026-05-31 | fix | ui-nyla | **Mobile FormGeneratorTable/Controls**: Controls stay horizontal (row wrap) on mobile instead of going vertical, reduced padding/gap/font-size on mobile for table cells and pagination - 2026-05-31 | fix | ui-nyla | **Chat DataSource attach sends DataSource ID**: SourcesTab now resolves DataSource UUID via ensureDataSourceId before attaching (was incorrectly sending connectionId) - 2026-05-31 | fix | platform-core | **TEXT to VECTOR schema migration**: Added auto-migration for TEXT->VECTOR columns in PostgreSQL connector, fixing broken semantic search on databases created before pgvector was declared - 2026-05-31 | fix | platform-core | **ChatDocument DB fallback**: getChatDocumentsFromDocumentList now queries ChatDocument table directly when in-memory workflow lookup fails, preventing orphaned document references - 2026-05-31 | fix | platform-core | **Table section string content normalization**: subStructureFilling validates table elements after AI JSON parse; string content (CSV/markdown/pipe) is parsed into {headers, rows} instead of crashing rendererMarkdown ## 2026-05-30 - 2026-05-30 | docs | wiki | **Depoformance Property Match — Backend ueber PORTA (Idee)**: 0-ideas-Dokument, das die Backend-Wuensche aus ihrer Techdoku (Kap. 11) in PORTA-Faehigkeiten uebersetzt statt Schlagworte woertlich zu nehmen. Mapping: 18 Provider-Interfaces -> generische RBAC-CRUD-Schicht; OpenRouter-Proxy -> unser AI-Gateway (serviceAi, kein OpenRouter); JWT-Felder -> PORTA Auth+RBAC (organizationId=mandateId); offene Punkte (Realtime/Upload/Mail/i18n/CI-CD) -> bereits PORTA-Plattformfunktionen; Security-Gate weitgehend abgedeckt. Keine Neutralisierung, CH-Residenz. Architektur: dedizierter Partner-Router (`/api/v1/depoformance/*`), der ihre Endpunkte durchschleift. Pricing: einmalig CHF 24'000 + API-Grundgebuehr CHF 600/Mo (inkl. 100k Calls) + AI pay-as-you-go. (c-work: 0-ideas/2026-05-depoformance-api-integration.md) ## 2026-05-29 - 2026-05-29 | docs | wiki | **Repo-Namen auf git.poweron.swiss/PowerOn migriert**: Wiki-weit alte Repo-/Pfad-Namen ersetzt (`frontend_nyla`/`frontend-nyla`→`ui-nyla`, `gateway`→`platform-core`, `private-llm`→`service-llm-private`). b-reference-Ordner umbenannt (gateway/ui-nyla/service-llm-private), README+product Komponenten-Tabellen aktualisiert, `service-preprocessing` ergaenzt, Changelog-Scope-Legende angepasst. Konzeptionelle Prosa-Begriffe (Gateway, Private-LLM) und z-archive/Changelog-Historie bewusst belassen. - 2026-05-29 | feat | platform-core | **Anthropic Claude Opus 4.8 hinzugefuegt**: `aicorePluginAnthropic.py` um Opus 4.8 (`claude-opus-4-8`) erweitert -- Basic- + Vision-Variante, 1M Context, 128K max output, gleiche Preise wie 4.7 ($5/$25 pro M Tokens). `_supportsCustomTemperature` erkennt 4.8 als Extended-Thinking-Modell (sendet kein `temperature`). - 2026-05-29 | feat | platform-core | **Private-LLM Next-Gen Modelle vorbereitet**: `aicorePluginPrivateLlm.py` um 3 neue Modell-Definitionen erweitert (deepseek-r1:70b Reasoning, llama4:scout Vision, nomic-embed-text Embedding). Werden automatisch aktiv wenn in Ollama verfuegbar -- kein Impact auf laufenden Betrieb. - 2026-05-29 | feat | private-llm | **Config fuer GPU-Migration vorbereitet**: `config.py` Next-Gen MODEL_MAPPING als kommentierter Block, `_isVisionModel` um llama4/scout erweitert. - 2026-05-29 | docs | wiki | **Private-LLM Architektur-Seite aktualisiert**: Migrations-Plan Infomaniak→FireStorm, geplante Modelle, Kostenvergleich dokumentiert. - 2026-05-29 | feat | ui-nyla | **SourcesTab sendToChat fuer Mobile**: Chat-Icon (💬) im Sources-Tab reaktiviert -- `onSendToChat_FeatureSource` und `onAttachDataSource` an FormGeneratorTree durchgereicht. Mobile-User koennen nun Source-Objekte per Tap in den Chat senden. - 2026-05-29 | fix | infra | **Gateway Workers auf 2 erhoeht** (prod+int): Single-Worker-Bottleneck behoben -- RAG-Indexierung blockierte User-Requests nicht mehr. ## 2026-05-28 - 2026-05-28 | fix | platform-core | **RAG ragLimits Vererbung**: `ragLimits` (z.B. `maxBytes`) auf der Connection-Root-DataSource (path `/`) wurden nicht an Kind-DataSources vererbt -- Walker fielen immer auf den 200 MB Default zurueck. Fix: `_loadRagEnabledDataSources` propagiert `settings.ragLimits` vom Connection-Root auf Kinder ohne eigene Werte. `_finalizeResult` in allen 4 Walkern (SharePoint, GDrive, kDrive, ClickUp) zeigt nun effektive Limits statt hartkodierter Defaults. - 2026-05-28 | feat | platform-core+ui-nyla | **DB Migration Streaming Import**: Import grosser JSON-Dateien (>1 GB) lief bisher komplett im RAM (`json.load()`), was OOM verursachte. Neuer Two-Pass-Ansatz mit `ijson` fuer speicher-effizientes Parsen: Pass 1 validiert + extrahiert Metadaten, Pass 2 splittet in per-Table JSONL-Dateien. Neuer `GET /process-import-stream` Endpoint mit `StreamingResponse` (ndjson) fuer Echtzeit-Fortschritt waehrend Validierung und Split. Frontend zeigt dynamischen Progress (Phase, DB, Tabelle, Datensatzzahl). - 2026-05-28 | feat | platform-core+ui-nyla | **DB Migration Streaming Export**: Export lief bisher client-seitig (`JSON.stringify`) und schlug bei grossen DBs fehl (`RangeError: Invalid string length`). Neuer `GET /export-stream` Endpoint streamt die gesamte Export-JSON inkrementell via `StreamingResponse`. Frontend nutzt `fetch()` + `ReadableStream` + File System Access API fuer direktes Streaming-to-Disk. Fortschrittslog zeigt aktuelle DB x/n mit MB-Angabe. - 2026-05-28 | fix | private-llm+infra | **LLM-Server Recovery**: Ollama-Modelle (`qwen2.5:7b`, `qwen2.5vl:7b`, `granite3.2-vision`) auf Infomaniak GPU-Server neu installiert (waren nach Instanz-Neustart verloren). NVIDIA-Treiber-Mismatch per Reboot behoben. pgvector Extension auf prod+int DB-Servern installiert (OS-Package + CREATE EXTENSION). Doku aktualisiert: `setupserver.md`, `README.md`, `infrastructure.md` (DB-Setup-Anleitung fuer neue Installationen). ## 2026-05-27 - 2026-05-27 | fix | platform-core | UDB DS Auto-Create: Virtuelle DataSource-Nodes (Browse-Folder ohne DB-Record) auf eigener Connection koennen jetzt getoggelt werden. `canEdit` prueft Connection-Ownership via `UserConnection.userId`; `setFlag` erstellt automatisch einen DataSource-Stub-Record analog zu `_findOrCreateTableFds`. Behebt 403-Errors beim Toggling von Browse-Foldern. 4 neue Tests (`test_udbNodes.py`). - 2026-05-27 | refactor | platform-core | UDB neutralizeFields-Handling von generischer Infrastruktur in polymorphe Node-Klassen verschoben: `_inheritFlags.cascadeResetDescendantsFds` ist wieder rein generisch (keine `neutralizeFields`-Logik); `_FdsFamilyNode.setFlag` bietet generischen `_onSetFlag`-Hook; `FdsTableNode._onSetFlag` wipe-t eigene `neutralizeFields` bei explizitem neutralize-Toggle; `FdsWorkspaceNode._onSetFlag` wipe-t `neutralizeFields` auf allen Descendant-Tables. `FdsFieldNode.getEffectiveFlag` erbt korrekt vom Table (Zwei-Quellen-Modell). 122/122 Tests gruen. Wiki `unified-data-bar.md` (Cascade-Reset-Abschnitt) aktualisiert. ## 2026-05-26 - 2026-05-26 | fix | gateway+frontend-nyla | UDB Toggle Spec Recovery: virtuelle Coordinates nutzen neu aggregate-Pfad (mixed moeglich ohne DB-Record); sechs redundante Aggregatoren + Parallel-Endpoint tree/attributes + Frontend refreshAttributes-Pfad entfernt; eine Pipeline, ein Resolver, immer refetch nach Toggle (Spec 2026-05-18 wiederhergestellt) (c-work: 3-validate/2026-05-udb-toggle-spec-recovery.md) ## 2026-05-24 - 2026-05-24 | docs | infra | **Deployment-Infrastruktur Doku** -- Neue kanonische Seite `b-reference/platform/infrastructure.md`: Infomaniak-Projektstruktur (Porta, LLM, Teamsbot), Naming Convention (`{bereich}-{env}-{komponente}`), VM-Instanzenliste, Deploy-Patterns. TOPICS.md ergaenzt. ## 2026-05-23 - 2026-05-23 | refactor | platform-core+ui-nyla+wiki | UDB Polymorphic Refactor: neue `UdbNode`-Klassenhierarchie (`udbNodes.py`), generischer Router `routeUdb.py` mit `POST /api/udb/tree/children` + `POST /api/udb/node/{key}/flag/{flag}`, Hart-Cut der 4 PATCH-Routen + altem `/api/workspace/{id}/tree/children`. FDS-Schema entschlackt (kein `userId`, kein `workspaceInstanceId`, kein `scope` mehr — feature-owned, RBAC-gated via `roleLabel.endswith('-admin')`). Neue kanonische Doku `b-reference/platform/unified-data-bar.md`; Updates auf `neutralization.md`, `rbac.md`, `TOPICS.md`. Tests: neu `test_udbNodes.py`, angepasst `test_buildTree.py` + `test_inheritFlags.py` + `UdbSourcesProvider.test.ts` (alle gruen) (c-work: 3-validate/2026-05-udb-polymorphic-refactor.md) - 2026-05-23 | feat | gateway+frontend | DB Migration Progress: Export und Import laufen per-DB sequentiell mit Echtzeit-Fortschrittslog (Timestamp, DB-Name, Tabellen-/Datensatzzahl, Fehler); neuer export-single Endpoint; Import via prepare-import + import-single (server-seitiger Payload-Cache mit Token); Export-Dateiname enthaelt Instanzlabel + full/partial (z.B. db_backup_main_full_2026-05-23T06-20-54.json) - 2026-05-23 | feat | gateway+frontend-nyla | **DB Migration Backup/Restore** — Neuer Tab "Migration" auf der Datenbank-Gesundheit-Seite (SysAdmin). Backup: dynamische DB-Auswahl via Registry, Export als JSON. Restore: JSON-Upload, Validierung, Import mit Modus "Neu" (replace) oder "Zusammenfuehren" (merge). System-Objekte (Root-Mandant, Admin-User, Event-User) werden nie geloescht; ihre IDs werden beim Import automatisch auf die Live-IDs remapped. Neue Dateien: `databaseMigration.py`, 4 API-Endpoints unter `/api/admin/database-health/migration/`, MigrationTab in `AdminDatabaseHealthPage.tsx`. ## 2026-05-22 - 2026-05-22 | fix | frontend-nyla | **UDB FormGeneratorTree: RAG-Icon + Mixed-Symbol nach Merge-Konflikt-Verlust restauriert** — In Commit `9488a7d` ("build errors") wurden bei einer Merge-Auflösung die RAG-Konstanten (`_RAG_ON_EMOJI` 🧠, `_RAG_OFF_EMOJI`), `_MIXED_SYMBOL` (◩), `_OFF_STATE_STYLE` sowie der RAG-Button-JSX-Block und die Mixed-/Spinner-Branches in Scope/Neutralize entfernt. Surgical Restore: alle Stellen wieder eingebaut, Idas Neutralize-On/Off-Emoji-Distinction und `hideRowActionButtons`/`dragDropEnabled`-Refactor beibehalten. Auch verlorenes Auto-Expand-`useEffect` für `defaultExpanded` Nodes wiederhergestellt (Test grün: 53/53). - 2026-05-22 | refactor | gateway+frontend-nyla | **RAG Cost-Estimate Währung USD→CHF** — `_costEstimate.estimateBootstrapCost()` liefert nun `estimatedChf` (statt `estimatedUsd`); Konstante `EMBEDDING_CHF_PER_MTOKEN` (Wert 0.02, Projekt-Konvention: Anbieter-Listenpreise werden direkt als CHF behandelt, identisch zu `calculatepriceCHF` in `aicorePluginOpenai.py`). Mit-aktualisiert: `routeDataSources.py` Docstring, `test_costEstimate.py` (6/6 grün), FE `CostEstimate`-Type, `DataSourceSettingsModal` Anzeige. Wiki: `b-reference/gateway/ai-agent.md` Endpoint-Doku, `TOPICS.md`, `c-work/2-build` + `4-done/2026-05-udb-datasource-settings.md`. ## 2026-05-19 - 2026-05-19 | fix | gateway | RAG Inventory /mandate: ImportError UserMandate behoben (war in datamodelUam statt datamodelMembership); refactored zu getUserMandatesByMandate(); RBAC membership-Check hinzugefuegt (verhindert Zugriff auf fremde Mandate via X-Mandate-Id) - 2026-05-19 | fix | gateway+frontend | RAG Inventory Mandate-Dropdown: neuer Endpunkt GET /api/rag/inventory/my-mandates liefert nur Mandate mit aktiver Mitgliedschaft; Frontend nutzt diesen statt /api/mandates/ (admin-only Route) - 2026-05-19 | feat | gateway+frontend | RAG Inventory Feature-Daten: featureInstances-Array mit Datei-/Chunk-Zaehler, Status-Breakdown (indexed/pending/failed), FeatureDataSource-Details mit ragIndexEnabled-Flag, RAG-Aktiv-Indikator; Frontend zeigt Sync-Status-Banner, Datenquellen pro Instanz, RAG-Hinweis wenn inaktiv; leere Instanzen werden ausgefiltert - 2026-05-19 | feat | gateway+frontend | Feature-Daten RAG-Sync: neuer Background-Job-Handler feature.bootstrap (subFeatureBootstrap.py) indexiert FeatureDataSource-Tabellendaten via FeatureDataProvider+requestIngestion; Handler-Registrierung in registerKnowledgeIngestionConsumer; neuer Endpoint POST /api/rag/inventory/reindex-feature/{workspaceInstanceId} mit FeatureAccess-Check; _buildFeatureInstanceInventory liefert runningJobs/lastSuccess/lastError; Frontend: Job-basierte Sync-Status-Banner + Reindex-Button fuer Feature-Instanzen (analog zu Connection-Sync) - 2026-05-19 | fix | gateway | FeatureDataProvider: alle DB-Zugriffe (getActualColumns, browseTable, aggregateTable, queryTable, _resolveInstanceColumn) von deprecated db.connection.cursor() auf db.borrowCursor() migriert (Pool-API-Kompatibilitaet nach Pooling-Refactoring) - 2026-05-19 | fix | gateway | Trustee Budget-Vergleich: _exportAccountingData liefert neu eine accountSummary (1 Zeile/Konto mit closingBalance + Q1-Q4) statt 5590 rohe Balance-Records (26/Konto); AI-Prompt praezisiert Datenquelle; reduziert Payload von 355KB auf 25KB und verhindert Faktor-100-Abweichungen durch versehentliches Summieren - 2026-05-19 | fix | frontend-nyla+gateway | **RAG-Inventar Mandate-Scope 403 + SQL-Fehler** — Zwei Bugs: (1) Frontend sendete `mandateId` als Query-Parameter statt `X-Mandate-Id`-Header → `context.mandateId=None` → 403. Fix: Header wird gesetzt. (2) Backend `routeRagInventory._getInventoryMandate` filterte `UserConnection` mit `recordFilter={"mandateId": ...}`, aber `UserConnection` hat kein `mandateId`-Feld → SQL-Error. Fix: Mandate-Members via `UserMandate`-Junction holen, dann deren `getUserConnections(userId)` aggregieren. - 2026-05-19 | refactor | gateway+frontend-nyla | **UDB Toggle Refresh: Visible-IDs Pattern** -- After a toggle, FE sends all visible node IDs in one POST request; backend returns correct attribute values (incl. mixed) for exactly those IDs. No full-tree reload, no optimistic updates. New endpoints: `POST /api/files/attributes` (FilesTab) and `POST /api/workspace/{id}/tree/attributes` (SourcesTab). `TreeNodeProvider.refreshAttributes(ids)` added to interface. Collapse now removes children from FE state (always fresh on re-expand). Removed `_enrichFoldersWithMixed` and `_refetchAllExpanded`. ## 2026-05-18 - 2026-05-18 | feat | gateway+frontend | UDB Resolve Endpoint: neuer POST /datasources/resolve-flags Bulk-Endpoint liefert effective-Werte (neutralize, scope, ragIndexEnabled) fuer beliebige Pfade auch ohne eigenen DB-Record; Frontend entfernt client-seitige Vererbungslogik (_findCoveringDs), nutzt ausschliesslich Backend-Daten via resolvedFlags Map - 2026-05-18 | fix+feat | gateway+frontend-nyla | **UDB Sources Recovery -- Zweite Smoke-Test-Runde (H1-H10)** (`c-work/3-validate/2026-05-udb-sources-recovery.md`). (H1) Logik-Audit aller drei Flags zusammengefasst: `'mixed'` ist Backend-Aggregate-Anzeige, niemals persistiert; FE-Handler mappen `'mixed'` immer auf konkreten Wert (`'personal'`/`false`); Cascade-PATCH setzt alle Nachkommen auf `NULL` und dann den Master, deshalb kann aggregate nach einem User-Toggle nicht `'mixed'` bleiben. (H2/H8) Initial-Render-Bug: sequentieller `for ... await` im Auto-Expand-Effekt loeste Cancellation-Race aus -- erstes `setNodes` triggerte cleanup, weitere defaultExpanded-Knoten blieben "expanded ohne Children". Fix: `Promise.all(...)` parallel + atomares Single-`setNodes`. Zusaetzlich `autoExpandedRef.current.clear()` in `_loadRoot` (StrictMode-Doppelmount + manuelle Refresh tauglich). (H3) `PUT /api/files/{id}` 404: Route hatte keinen `RequestContext`, `interfaceDbManagement.getInterface(currentUser)` ohne mandate/featureInstance scope -> RBAC-Filter excludiert File. Fix: Context-Dep + scope-pass-through bei `update_file` und `delete_file`; `move_folder` akzeptiert sowohl `parentId` als auch `targetParentId` aus dem Body. (H4) Neuer Folder erschien initial auf Legacy-Top-Level: `FolderFileProvider.createChild(parentId=null, ...)` setzt jetzt `parentId = _SYNTH_ROOT_ID('own')` damit der neue Folder unter `/` rendert. (H5) `+`-Button pro Folder im FormGeneratorTree: neue `onCreateChild?: (parentId: string) => void`-Prop in TreeNodeRow + verdrahtetem `_createFolderAt(parentId)` Handler in der Hauptkomponente; sichtbar im Hover-Action-Slot fuer alle Folders mit `provider.createChild` && `provider.canCreate(id)`. (H6) FilesTab neutralize partly broken: Diagnose plausibel mit H2-Fix erledigt (Optimistic-Update verfehlte Knoten, deren Children durch das Cancellation-Race nicht im FE-State waren). (H7) Hardcoded `[Persoenliche Quellen]`: Source-String hatte `oe`-Encoding, `t()` returned key fuer DE -> sichtbar mit fake-Umlaut. Fix: `resolveTextSafe("Persoenliche Quellen")` -> `resolveTextSafe("Persönliche Quellen")` (echter Umlaut). (H9) FDS-Neutralize ging nicht: `POST /api/workspace/{instanceId}/feature-datasources` 403te Cross-Mandate-Erstellung (Workspace mandate A, Feature mandate B beides user-zugaenglich), `_ensureRecord` schluckte den Fehler still. Fix: Cross-Mandate-Block entfernt, statt dessen `getFeatureAccess(userId, body.featureInstanceId)`-Validierung; `mandateId` der neuen FDS = `wsMandateId` (= Workspace-Tenancy, konsistent mit Tree-Filter). (H10 = G5) Persistenter Expand-State umgesetzt: `WorkspaceUserSettings.uiTreeExpansion: Dict[str, List[str]]` Backend-Field; neue Routen `GET/PUT /api/workspace/{instanceId}/ui-tree-expansion/{scope}`; FE-Hook `useTreeExpansion(instanceId, scope)` mit 600 ms Debounce-PUT; `FormGeneratorTree` Props `expandedIds?: string[] | null` + `onExpandedIdsChange?: (ids) => void` (controlled mode); `SourcesTab` (scope `'sources'`), `FilesTab` (scopes `'filesOwn'` + `'filesShared'`) verdrahtet. Bei `null` (kein Record) Default-Verhalten + erster Toggle erstellt den Settings-Record; bei Array gewinnt persistierte Liste ueber Backend-`defaultExpanded`-Hints. Tests: Backend `services+routes` 107/107 gruen, Frontend UDB+FormGen 69/69 gruen. - 2026-05-18 | fix+feat | gateway+frontend-nyla | **UDB Sources Recovery -- Smoke-Test-Followups (G1-G5)** (`c-work/3-validate/2026-05-udb-sources-recovery.md`). (G1) `tsconfig.json` referenziert jetzt auch `tsconfig.test.json`; Testfile bekam expliziten `import React`, `afterEach`, `@testing-library/jest-dom/vitest`. 139 -> 0 Lints; Tests 50/50 weiterhin gruen. (G2) Off-State der `neutralize`/`ragIndexEnabled`-Buttons: `filter: grayscale(1); opacity: .45` im OFF-Zustand -- offenes Schloss + greyed-Brain klar als "deaktiviert" lesbar. (G3a) Bug `_browseChildren`: Childs erbten `parentKey=ds|...|/` statt des aufgerufenen `svc|...`; Children erschienen daher nie im FE-Tree. Fix: `parentKey` als optionaler Parameter durchgereicht; Dispatcher uebergibt den asked-for-Key. (G3b) Per-Field-Neutralize fuer Feature-Tabellen: `fdsTable.hasChildren=True` bei vorhandener `meta.fields`-Liste; neuer Knoten-Typ `fdsField` (Dispatcher `fdstbl|fi|table` -> `_featureTableFields`). Effektiver Field-Neutralize = `parent.neutralize OR field IN parent.neutralizeFields`. Toggle: `UdbSourcesProvider.patchNeutralize` splittet die Batch nach Kind und ruft fuer fdsField `PATCH /api/datasources/{id}/neutralize-fields` mit der mutierten Liste; `neutralizeFields` ist im Tree-Payload mitgesendet -- kein Extra-GET. Scope/RAG auf Feldebene bewusst nicht editierbar. +3 neue Backend-Tests in `TestFeatureTableFields`. (G4) FilesTab synthetischer "/"-Root pro Ownership: Provider mappt `parentId=null` auf `[__filesRoot:]` (`defaultExpanded=true`); reale Top-Level-Items werden Children. `moveNodes` mit Synth-Root-Ziel re-mapt auf `parentId/folderId=null` (= Drop auf "/"); `patchScope`/`patchNeutralize` mit Synth-Root-Id materialisieren ueber API alle Top-Level-Folders+Files und setzen pro Folder `cascadeChildren=true` -- globaler Toggle ohne Backend-Aenderung. Tests: Backend 19/19 gruen, Frontend UDB+FormGen 69/69 gruen. - 2026-05-18 | refactor | gateway+frontend-nyla | **UDB Sources Recovery -- Folge-UX-Iteration** (`c-work/3-validate/2026-05-udb-sources-recovery.md`). Auf das User-Feedback nach Phase 1-3: (a) **Visual cascade fuer `neutralize` und `ragIndexEnabled`** wieder klar erkennbar -- vorher *gleiches Symbol* fuer on/off (nur Opacity), Cascade unsichtbar. Distinct emojis: `_NEUTRALIZE_ON_EMOJI=closed lock` / `_NEUTRALIZE_OFF_EMOJI=open lock`, `_RAG_ON_EMOJI=brain` / `_RAG_OFF_EMOJI=thought-bubble`. Backend-Cascade war korrekt (parametrisch in `cascadeResetDescendants(rec, flag)`); rein UI-Issue. (b) **Top-Level-Layout flach**: synthetische Wrapper `srcRoot` + `mandateRoot` entfernt. `_topLevel` emittiert direkt `[personalRoot, mgrp|m1, mgrp|m2, ...]`. Mandate-Groups haben nun `parentKey=None`. (c) **`TreeNode.defaultExpanded?: boolean`** als generisches Tree-Feature (one-shot pro Node-Id ueber `autoExpandedRef`); BE markiert `personalRoot` und alle Mandate-Groups mit `defaultExpanded=True` -- UI oeffnet bis zur Datenquellen-Ebene ohne User-Klick. (d) **Icon-Reihenfolge** in `FormGeneratorTree` (rechtsbuendig, von rechts nach links): neutralize, scope, sendToChat, rag, settings. Settings (`extraActions`) jetzt linksbuendig. (e) **Settings-Icon nur auf Daten-Quellen-Root** (`kind in {connection, featureNode}`), aber dort *immer* sichtbar, auch ohne `dataSourceId`; Klick triggert lazy `_ensureRecord` -> `onOpenSettings`. (f) **`SourcesTab`** setzt `title={t('Datenquellen')}` als Section-Header. Tests: Backend `test_buildTree.py` 16/16 + `test_inheritFlags.py` 72/72 -> 88/88 gruen. Frontend `FormGeneratorTree.test.tsx` 50/50 (+`defaultExpanded`-Tests) + `UdbSourcesProvider.test.ts` 16/16 (+settings-on-root + defaultExpanded-passthrough). TypeScript clean. - 2026-05-18 | refactor | gateway+frontend-nyla | **UDB Sources Recovery: SourcesTab nutzt FormGeneratorTree** (`c-work/3-validate/2026-05-udb-sources-recovery.md`). Behebt das Folge-Problem aus dem Generic-Tree-Refactor vom selben Tag, dass der Sources-Tab nur "Lade Datenquellen..." zeigte, weil der ad-hoc `_TreeNodeView`-Renderer in `SourcesTab.tsx` an einem State-Race (StrictMode-Doppelmount × `mountedRef`-Frueh-Abort × paralleler `expansionSignature`-useEffect, vier sichtbare POSTs) erstickte. Diagnose via temporaerem `_buildTree.getChildrenForParents`-INFO-Log: Backend liefert sauber `counts={'__root__': 6}` -- Bug war ausschliesslich auf der ad-hoc-Renderer-Seite. **Backend** (`_buildTree.py`): synthetische Container `srcRoot` (Top-Level), `personalRoot`, `mandateRoot` mit neuem `_syntheticNode` Helper und `displayOrder`-Sort-Hint; `_topLevel` schrumpft auf `[srcRoot]`, `_srcRootChildren` emittiert `[personalRoot, mandateRoot]`, `_personalRootChildren` und `_mandateRootChildren` enthalten die alte Connections-/Mandate-Logik mit korrigierten `parentKey`. Dispatcher in `getChildrenForParents` matcht `parentKey == _KEY_*_ROOT` vor dem Decode-Pfad. +7 neue `TestSyntheticRoots` Tests, Service-Suite 89/89 gruen. **FormGeneratorTree** (generisch erweitert, kein UDB-Vokabular): `TreeNode.ragIndexEnabled?: boolean | 'mixed'`, `TreeNode.displayOrder?: number`, `TreeNodeProvider.patchRagIndex?` + `canPatchRagIndex?`, `FormGeneratorTreeProps.refreshAfterAction?: boolean`. Sort-Comparator setzt `displayOrder` vor folder-first. Dritter Flag-Button (`_RAG_EMOJI`, `_ACTION_RAG`) rendert nur wenn `ragIndexEnabled !== undefined`. `_runAction` ruft `_refetchAllExpanded` automatisch auf wenn `refreshAfterAction=true`; optimistic-Update-Pfad fuer FilesTab unveraendert. +10 neue Tests, Tree-Suite 51/51 gruen. **Frontend SourcesTab**: ad-hoc-Renderer mit `_TreeNodeView`, `_FlagButton`, eigenem `pendingToggles`-State, eigenem `_fetchVisible`-Pfad komplett geloescht. Neuer `UdbSourcesProvider.tsx` mappt Backend-`UdbBackendNode` auf generischen `TreeNode` (Synthese-Container ohne Flag-Felder); haelt internen `nodeCache: Map` fuer Patch-Pfade; `_ensureRecord` POSTet `/datasources` oder `/feature-datasources` je nach `kind`; danach PATCH `/api/datasources/{id}/{scope|neutralize|rag-index}`. Neue `SourcesTab.tsx` ist 75 LOC duenner Wrapper: `useMemo` Provider + `` + Settings-Modal-Anbindung ueber `extraActions`. +14 neue Provider-Tests, Tree+Provider-Suite 65/65 gruen. - 2026-05-18 | fix | gateway | PostgreSQL `recordCreate`/`_save_record`: Zeichenketten-Parameter mit eingebetteten NUL-Bytes (`\\x00`) werden vor dem Bind gestriped — behebt `ingestion.failed` / `ContentChunk`-Insert (psycopg2: „A string literal cannot contain NUL“), z. B. bei indexierten `.sql`/Dump-Dateien. - 2026-05-18 | refactor | gateway+frontend-nyla | **UDB Generic Tree Refactor: Backend autoritativ, FE pure Renderer, RAG fuer FDS** (`c-work/4-done/2026-05-udb-generic-tree-refactor.md`). Schliesst die ueber mehrere Iterationen verschleppte UI-Inkonsistenz, dass Toggles unter bestimmten Bedingungen keinen UI-Effekt hatten. Root-Cause: parallele Logik im Frontend (`_resolveFdsFlags`, optimistic Updates, `_findCoveringDs`) widersprach den vom Backend gelieferten Werten. **Architektur**: Ein einziger Endpoint `POST /api/workspace/{instanceId}/tree/children` mit Body `{parents: [null, ...expandedKeys]}` liefert `nodesByParent` als flachen Map mit allen drei `effective*`-Werten (`neutralize`, `scope`, `ragIndexEnabled` als `boolean|'mixed'` bzw. `string|'mixed'`) pre-computed auf der `mode='aggregate'`-Ebene. Neuer Orchestrator `serviceKnowledge/_buildTree.py` mit stabilem Key-Format (`conn|`, `svc||`, `ds|||`, `mgrp|`, `feat|||`, `fdstbl||`) — preloadet DS+FDS einmal pro Request und dispatched pro Parent-Kind an Connector/Catalog/RBAC. Sourcetype-Mapping (`sharepoint -> sharepointFolder` etc.) ist nun Backend-autoritativ. **FE-Refactor**: `SourcesTab.tsx` von ~2500 auf ~530 Zeilen reduziert. State nur noch `childrenByParent: Map`, `expandedKeys: Set`, `pendingToggles: Set`. Toggle-Flow strikt PATCH -> Refetch -> Render mit Spinner ueber dem Flag-Button bis API-Response zurueck ist. Generischer `_TreeNodeView` rekursiv fuer ALLE Kinds, generischer `_FlagButton` mit einheitlichem Mixed-Symbol (`U+25E9`, "square with diagonal lines") fuer alle drei Flags. **RAG fuer FDS**: `_INHERITABLE_FDS_FLAGS` enthaelt nun `ragIndexEnabled`; `FeatureDataSource.ragIndexEnabled: Optional[bool]` Field (Auto-Migration via `ALTER TABLE ADD COLUMN`); `PATCH /api/datasources/{id}/rag-index` akzeptiert via `_findSourceRecord` sowohl DS als auch FDS (Bootstrap-Job + Chunk-Purge bleiben DS-only); `GET /feature-datasources` liefert `effectiveRagIndexEnabled`; `resolveEffectiveForFds` um den dritten Wert ergaenzt. **Geloescht** (keine Konsumenten mehr): `POST /datasources/resolve-flags` + 3 Models, `GET /connections`, `GET /connections/{id}/services`, `GET /connections/{id}/browse`, `GET /feature-connections`, `GET /feature-connections/{fiId}/tables`, `GET /feature-connections/{fiId}/parent-objects/{tableName}`, totes `SourcesTab.module.css`. **Bewusste Reduzierung** (User-Entscheidung): FDS-Records sind nicht mehr expandierbar (`hasChildren: false` fuer `fdsTable`); Tabellen-Ebene reicht. `FeatureDataSource.recordFilter` bleibt fuer API-Kompat erhalten. **Tests**: neu `test_buildTree.py` (10 gruen — Key-Coding, Effective-Triplets, Record-Lookup, Orchestrator-Smoke), +5 neue in `test_inheritFlags.py::TestResolveEffectiveForFds` (RAG inherits, RAG aggregate-mixed, `_INHERITABLE_FDS_FLAGS` enthaelt RAG) +1 in `TestCascadeResetFdsRag`. Resultat: 82/82 gruen; Frontend `npx tsc --noEmit` clean. - 2026-05-18 | feat | gateway+frontend-nyla | **UDB Toggle-Semantik v2: 'mixed'-Aggregation + bottom-up cascade + Backend-autoritative effective values** — Grundlegendes Redesign der Toggle-Semantik (neutralize, scope, ragIndexEnabled) für DataSource und FeatureDataSource. (1) Backend: `getEffectiveFlag` und `getEffectiveFlagFds` mit `mode='walk'|'aggregate'` — walk gibt immer konkret, aggregate gibt 'mixed' wenn Subtree divergiert. (2) `cascadeResetDescendants`/`Fds` jetzt bottom-up (deepest-first) für Crash-Sicherheit, Rückgabe `List[str]` statt `int`. (3) GET /datasources und /feature-datasources liefern computed `effectiveNeutralize`, `effectiveScope`, `effectiveRagIndexEnabled` pro Item. (4) PATCH-Response enthält `resetDescendantIds` + `updatedAncestors` für optimistic UI. (5) Bug-Fixes B1-B6: routeDataConnections consent-re-enable RAG, routeRagInventory reindex+inventory, routeDataSources settings-RBAC, _dataSourceTools download neutralize, _featureSubAgentTools FDS neutralize cascade — alle auf `getEffectiveFlag(mode='walk')` umgestellt. (6) Frontend: Workarounds entfernt (`_effectiveFlag`, `_findAncestorDs`, `_AUTHORITY_SOURCE_TYPES`, prop-drilling `inheritedScope/Neutralize`), zentrale `_readDsFlags`/`_readFdsFlags` aus Backend-Werten, Toggle-Handler mit optimistic cascade via `resetDescendantIds`. (7) 'mixed'-Icon-Darstellung im UI. - 2026-05-18 | chore | gateway | RAG-Walker und `requestIngestion`: per-item Lognois reduziert — `subWalkerHelpers` (`walker.item.start`, `walker.download.start`, `walker.extract.start`) und `ingestion.skipped.duplicate` von INFO auf DEBUG, damit Daily-Resync / wiederholtes Durchlaufen nicht das App-Log flutet; Stuck-Triage via DEBUG auf `modules.serviceCenter.services.serviceKnowledge.subWalkerHelpers` bzw. `mainServiceKnowledge`. - 2026-05-18 | fix | gateway+frontend-nyla | **UDB Cascade-Inherit Bugfix: Connection-Root Toggle hatte keinen UI-Effekt** — User-Report: "no change in UI, whatever icon I press". Log-Analyse zeigte: jeder Toggle macht POST `/datasources` (Backend gibt via Upsert die bereits existierende ID zurück) gefolgt von PATCH (Backend updated korrekt), aber UI bleibt unverändert. **Root-Cause 1**: Die ConnectionRoot-DataSource (`sourceType='msft'`/`'google'`/`'clickup'`/`'infomaniak'`/`'local'`, `path='/'`) wurde im Frontend von `_findDs` ignoriert (`if (node.type === 'connection') return undefined`) — Workaround aus dem Vortag, der eine andere Race-Condition verhindern sollte. Folge: `ds === undefined` für Connection-Nodes → `_addAsDataSource`-POST → Backend Upsert findet bestehenden DS via `connectionId+path`-Match und returnt dieselbe ID → PATCH erfolgreich → UI rendert aber weiter mit `ds=undefined` → opacity bleibt 0.35. **Fix 1**: `_findDs` matcht jetzt deterministisch via `sourceType`: für Connection-Nodes `node.authority` ('msft'/'google'/…), für Service-Nodes `_SERVICE_TO_SOURCE_TYPE[node.service]` ('sharepointFolder'/…). Zwei DS mit `connectionId+path='/'` aber unterschiedlichem `sourceType` (Connection-Root vs Service-Root) sind dadurch eindeutig unterscheidbar. **Root-Cause 2**: Cross-Authority-Vererbung fehlte komplett. User-Erwartung: Toggle auf Connection-Root muss alle Service-Children (SharePoint, OneDrive, Outlook unter derselben msft-Connection) visuell mitziehen. Backend `getEffectiveFlag` und `cascadeResetDescendants` filterten aber strikt auf `sourceType==parentSourceType`, also keine Vererbung über die Authority-Grenze hinweg. **Fix 2**: Neue Konstante `_AUTHORITY_SOURCE_TYPES = {'local','google','msft','clickup','infomaniak'}` in `_inheritFlags.py` und Mirror im Frontend. `_findAncestorChain` ergänzt um den ConnectionRoot als "äusseren" Ancestor (after all same-sourceType ancestors). `cascadeResetDescendants` cascadiert bei `parentIsConnectionRoot=True` über die GANZE Connection (cross-sourceType). Frontend `_findAncestorDs` analog. **Tests**: 23/23 grün (4 neue für Cross-Authority: `test_connection_root_inherits_cross_sourcetype`, `test_same_sourcetype_ancestor_wins_over_connection_root`, `test_connection_root_does_not_self_inherit`, `test_connection_root_cascades_cross_sourcetype`). Frontend `npm run build` + `tsc --noEmit` grün. - 2026-05-18 | feat | gateway+frontend-nyla | **UDB Cascade-Inherit für DataSource-Flags (`neutralize`, `ragIndexEnabled`, `scope`)** — Plan: `c-work/1-plan/2026-05-udb-cascade-inherit.md`. Schliesst die Konsistenz-Lücke, dass Toggles an einem Parent nicht nach unten propagieren konnten, sobald ein Descendant einen eigenen DataSource-Record hatte. Beispiel-Bug: SharePoint=true → Folder1=false (explizit) → SharePoint=true (zweites Toggle) → Folder1 blieb auf false statt mit dem Parent zu folgen. **Architektur**: 3-wertige Felder mit `NULL = inherit`, expliziter `True/False` (oder `'personal'/'mandate'/'platform'/'global'` für scope). **Backend**: (1) Datenmodell `DataSource.{neutralize,ragIndexEnabled,scope}` und `FeatureDataSource.{neutralize,scope}` auf `Optional[...]` umgestellt; Migration `script_db_migrate_datasource_inherit.py` macht Spalten nullable (idempotent, additiv — bestehende Werte bleiben explizit, NEW records starten mit NULL). (2) Neuer zentraler Helper `serviceKnowledge/_inheritFlags.py` mit `getEffectiveFlag(rec, flag, allDs)` (path-traversal aufwärts, longest-prefix wins, defensiv `connectionId` UND `sourceType` als Filter), `cascadeResetDescendants(rootIf, parentRec, flag)` (setzt nur das EINE Flag der explizit-belegten Descendants auf NULL, andere Flags unangetastet) und `_isAncestorPath` (`/` ist Ancestor von allem; `/foo` NICHT von `/foobar` — `/`-Separator-Pflicht). (3) PATCH-Endpoints `routeDataSources.{_updateDataSourceScope,_updateDataSourceNeutralize,_updateDataSourceRagIndex}` akzeptieren `Optional[bool|str]` als Body-Wert: `null` → reset auf inherit (kein Cascade); `true/false` → explizit + Cascade-Reset aller Descendants. RAG-Endpoint zusätzlich: nur bei `True` Mini-Bootstrap-Job + `_ensureConnectionKnowledgeFlag`; nur bei `False` synchrone Chunk-Purge — `null` no-op. Audit-Log `rag_index_toggled` trägt `cascadedDescendants` Count. (4) Walker-Integration: `_loadRagEnabledDataSources` filtert auf **effective** ragIndexEnabled (via `getEffectiveFlag`) und schreibt resolved Werte für `neutralize/scope` direkt in das returned dict — Walker (Sharepoint/Outlook/Gdrive/Gmail/Clickup/Kdrive) bleiben unverändert und lesen weiter `ds.get("neutralize", False)`. Alter `subPolicyResolver.py` zu Backward-Compat-Shim auf `getEffectiveFlag` deprecated. **Frontend**: `UdbDataSource` Interface mit `boolean|null` und `string|null`; neue Helper `_findAncestorDs` + `_effectiveFlag` (path-traversal analog zu Backend); Toggle-Handler senden `!currentEffective` (= aktuell sichtbarer Wert wird invertiert), Backend macht den Cascade. Local State: optimistisch + `_fetchDataSources()` nach PATCH damit cascade-resettete Descendants aus DB neu geladen werden. Alte unbenutzte `_togglePersonalNeutralize/_togglePersonalRagIndex/_cyclePersonalScope` entfernt. **Tests**: neuer `test_inheritFlags.py` mit 19 grün — explicit-wins, ancestor-traversal, default-fallback, sourceType-Isolation, connectionId-Isolation, `/foo` ≠ Ancestor von `/foobar`, root-is-ancestor-of-everything, scope-string-default, cascade-touches-only-target-flag, cascade-skips-other-sourcetypes. Bestehende `test_knowledge_ingest_consumer.py` 8/8 grün. Frontend `npm run build` grün, TypeScript clean. **Pre-existing Test-Failures** (nicht durch diesen PR): `test_p1d_consent_prefs.py` (5) — Python-3.13 asyncio-Deprecation + Schema-Drift `ConnectionIngestionPrefs.neutralizeBeforeEmbed`; `test_bootstrap_sharepoint.py` + `test_bootstrap_gmail.py` (5) — rufen `bootstrapSharepoint/Gmail` ohne `dataSources=` auf, was seit dem 2026-04 Unified-Indexing-PR den Early-Return triggert. ## 2026-05-17 - 2026-05-17 | fix | frontend-nyla | **UDB Settings-Modal: RAG-Limits nur auf DataSource-Root** — Settings-Icon (⚙️) bleibt auf allen Nodes sichtbar, aber RAG-Limits- und Kostenschätzungs-Sektionen werden nur noch auf DataSource-Root-Nodes (Level 2 = `service`) angezeigt. Subelemente (Folder/File) können weiterhin die Connection-Settings sehen, erben aber die Walker-Limits vom Root. Neue Modal-Prop `showRagSection`. Neutralisierung/RAG-Toggle: Vererbungslogik ist korrekt (Parent aktiviert → Kinder werden mitgezogen, volle Opacity). Kein visueller Unterschied nötig — das ist gewolltes Verhalten. - 2026-05-17 | feat | gateway+frontend-nyla | **i18n für BackgroundJob-Progress-Messages (Backend-translated)** — User-Report: RAG-Page zeigte "145 Dateien verarbeitet, 106 indexiert" auch bei UI-Sprache=`en`, weil walker das Plaintext-Deutsch direkt in `BackgroundJob.progressMessage` schrieben und das Frontend es 1:1 rendert. Root-Cause: BackgroundWorker hat keinen Request-Sprach-Kontext (`_CURRENT_LANGUAGE` ist ContextVar pro Request), und `progressMessage` wird persistiert — wäre selbst dann gefroren, wenn der User später die Sprache wechselt. **Architektur (regelkonform zu `wiki/b-reference/gateway/architecture.md#i18n`):** Backend speichert strukturiert + übersetzt server-side beim Route-Read; Frontend rendert 1:1 — kein `t()` auf Backend-Werten. (1) Neue JSONB-Spalte `BackgroundJob.progressMessageData = {key, params}` (Migration `script_db_migrate_backgroundjob_progress_data.py`, additiv + idempotent). (2) `JobProgressCallback.__call__` akzeptiert `messageKey="LITERAL"` + `messageParams={…}` und schreibt beides als JSON; zusätzlich rendert es einen DE-Fallback in `progressMessage` für Logs/Audit/Legacy-Clients. (3) Alle Walker (6 RAG + `subConnectorIngestConsumer` + Trustee push/sync/import + `accountingDataSync._progress`) umgestellt — `messageKey=` ist immer ein String-Literal. (4) Key-Registrierung über string-literale `t("…")` Calls: neues `serviceKnowledge/_progressMessages.py` (Side-Effect-Import in `app.py` lifespan, 5 RAG-Keys), Trustee 14 Keys in `mainTrustee.py` — KEINE Variable-Aufrufe von `t()` (Wiki-Regel #1: `t(variable)` ist verboten). (5) Neuer Helper `i18nRegistry.resolveJobMessage(messageData)` analog zu `resolveText(value)` — der einzige zulässige `t(variable)`-Pfad, weil er in der i18n-Infrastruktur lebt; nutzt `_CURRENT_LANGUAGE` aus dem Request-Kontext und substituiert Params via `.format(**params)`. (6) `routeJobs._serialiseJob` und `routeRagInventory` rufen `resolveJobMessage` beim Read und schreiben das Ergebnis in `progressMessage` — Frontend bekommt einen fertigen, übersetzten String. (7) Frontend zurückgebaut: `utils/jobProgressUtils.ts` Helper **gelöscht**, DTOs (`useBackgroundJob`, `connectionApi`, `trusteeApi`) ohne `progressMessageData`-Feld, Render-Stellen (`RagInventoryPage`, `RagRunningBadge`, `TrusteeAccountingSettingsView`) lesen direkt `job.progressMessage`. Tests: 22/26 grün; die 4 Failures in `test_knowledge_ingest_consumer.py` sind pre-existing (verifiziert via `git stash` Diff). Frontend `npm run build` grün. Smoke: `resolveJobMessage({'key': '{n} Dateien verarbeitet, {indexed} indexiert', 'params': {'n': 145, 'indexed': 106}})` → `'145 Dateien verarbeitet, 106 indexiert'`. Wiki: neuer Abschnitt "BackgroundJob-Progress-Messages" in `b-reference/gateway/architecture.md` mit den 4 Schritten (Walker → Registrierung → Route-Resolve → Frontend-Render). - 2026-05-17 | feat | gateway+frontend-nyla | **UDB DataSource Settings (⚙️) + konfigurierbare RAG-Limits** (Plan & Build: `c-work/2-build/2026-05-udb-datasource-settings.md`). Schliesst zwei Lücken: (1) RAG-Walker-Limits (`maxBytes=200 MB` etc.) waren hartkodiert — User mit 500-MB-Folder konnte nur Code-Änderung machen; (2) FeatureDataSource hatte gar keinen Settings-Ort. **Backend**: JSONB-Spalte `settings` auf `DataSource` + `FeatureDataSource` (Migration `script_db_migrate_datasource_settings.py`, additiv + idempotent). Neues Modul `serviceKnowledge/_ragLimits.py` mit `FILES_LIMITS_DEFAULT` / `CLICKUP_LIMITS_DEFAULT` als zentrale Source-of-Truth — die alten `MAX_*_DEFAULT`-Konstanten in den 4 Walkern (`subConnectorSyncSharepoint/Kdrive/Gdrive/Clickup.py`) sind nur noch Aliase. Kritische Semantik: `getStoredOverrides(ds, kind)` liefert NUR explizit gesetzte Overrides → Walker mergen sie auf den **caller-supplied** `limits=`-Parameter, damit Test-/Caller-Overrides weiter gewinnen (`test_bootstrap_maxTasks_caps_ingestion=3` bleibt grün); `getRagLimits(ds, kind)` mergt auf Defaults → API/Cost-Estimate-Pfad. **Keine Override-Schicht, keine Resolver-Logik** — was im Modal steht, ist exakt was der Walker liest. Zwei neue Endpunkte in `routeDataSources.py`: `PATCH /api/datasources/{id}/settings` (akzeptiert nur Top-Level-Key `ragLimits`, unknown → 400, positive Ints only, Owner-only/Mandate-Admin, Audit-Log `datasource_settings_changed`) und `GET /api/datasources/{id}/cost-estimate` (indikative USD-Schätzung via `_costEstimate.py`-Heuristik: `text-embedding-3-small @ $0.02/1M Token`, `BYTES_PER_TOKEN=4`, `EXTRACTABLE_FRACTION=0.4`; Antwort trägt vollständiges `basis`-Objekt mit Annahmen/Formel/Notes). **Frontend**: Neues ⚙️-Icon pro Node im `UnifiedDataBar/SourcesTab.tsx` (vor dem 🧠) öffnet den neuen `DataSourceSettingsModal.tsx` mit drei klar abgegrenzten Sektionen: (1) **Connection** — `knowledgeIngestionEnabled`-Toggle via `patchKnowledgeConsent` (mit Confirm-Dialog beim Deaktivieren); (2) **RAG-Limits** — Felder editierbar, Bytes in MB im UI; (3) **Kostenschätzung** — refresh nach Save. Dasselbe Modal wird auf der `RagInventoryPage.tsx` vom amber Partial-Banner (`stoppedAtLimit`) via neuen "Limit anpassen"-Button geöffnet → User hat direkten Pfad vom Symptom zur Behebung. Workspace-Route `GET /api/workspace/{instanceId}/connections` liefert jetzt `knowledgeIngestionEnabled` mit, damit der Modal-Initial-Toggle korrekt vorbelegt. **Tests**: 12 neue Unit-Tests in `tests/unit/services/test_ragLimits.py` + 6 in `test_costEstimate.py` (Defaults-Isolation, partial-override, non-int dropped, doubling-formula, basis-shape) — alle grün; bestehende Bootstrap-Tests für Sharepoint/Kdrive/Gdrive/ClickUp weiter grün (caller-limits-Override respektiert). Frontend `npm run build` grün, keine neuen Lint-Errors. Doku: `b-reference/gateway/ai-agent.md` (Abschnitt "Konfigurierbare RAG-Limits"), `TOPICS.md` (neuer Eintrag). Verbleibende Hard-Limits in `subConnectorSyncOutlook/Gmail.py` haben aktuell kein UI-Override, bleiben aber als next-step (gleicher Helper anwendbar). - 2026-05-17 | feat | frontend-nyla+gateway | **Knowledge-Consent Toggle auf ConnectionsPage + Forward-Sync DataSource→Connection.** Zwei Lücken in der RAG-Consent-UX geschlossen, die zu der Beobachtung "valueon hat Index aktiviert, aber Checkbox fehlt" geführt haben: (1) Die ConnectionsPage zeigte `knowledgeIngestionEnabled` nur als generische "Ja/Nein"-Spalte der FormGeneratorTable — kein Toggle-Element. Neu: zwei CustomActions (`FaToggleOn`/`FaToggleOff`, je nach State sichtbar via `visible`-Filter), Klick ruft `patchKnowledgeConsent` → `/api/connections/{id}/knowledge-consent` und refetcht die Liste. Damit ist die UI 1:1 konsistent mit dem Master-Switch auf der RagInventoryPage (gleiches Backend-Endpoint, gleiches Icon, gleicher Confirm-Dialog beim Deaktivieren). (2) Backend: `routeDataSources._updateDataSourceRagIndex` propagierte bisher nicht auf die Parent-Connection. Neuer Helper `_ensureConnectionKnowledgeFlag(rootIf, connectionId)` setzt **forward-only** `UserConnection.knowledgeIngestionEnabled=True`, sobald min. eine DataSource auf `ragIndexEnabled=true` toggelt — kein Auto-Disable, weil der Master-Switch dem User gehört (verhindert versehentliches Zurücksetzen eines explizit gegebenen Consents, z.B. einer Connection ohne aktive DataSource, aber mit `knowledgePreferences`). Plan-Doc für die UDB-Settings-Erweiterung (Issue 2): `c-work/1-plan/2026-05-udb-datasource-settings.md`. Der Fix für Limit-Transparenz wirkte UI-seitig nicht, weil `_bootstrapJobHandler` in `subConnectorIngestConsumer.py` die Sub-Service-Results in ein wrappendes Dict packt (`{"authority", "connectionId", "sharepoint": {...}, "outlook": {...}}` für msft; analog für `drive`/`gmail`/`clickup`/`kdrive`). `routeRagInventory._buildConnectionInventory` griff aber auf Top-Level `result.stoppedAtLimit`/`indexed`/etc. zu — alle `None`. Folge: amber Banner blieb aus UND die Statistik-Zeile zeigte gar keine Zahlen ("Sync erfolgreich" ohne "— 25 unverändert"). Neuer `_flattenJobResult()`-Helper aggregiert über alle bekannten Sub-Keys (sum für counters, max für durationMs, erstes Limit-Hit für `stoppedAtLimit`/`limits`). Verifiziert anhand Job `2374aecd-3e17-460a-a13e-530f9f1115e6`: `bytesProcessed=209894527` ≥ `maxBytes=209715200`, jetzt korrekt als `stoppedAtLimit="maxBytes"` an die UI durchgereicht. Diagnose-Skript `gateway/scripts/debug_rag_job_result.py` zeigt vor/nach-Flatten und bleibt für künftige Bootstrap-Result-Debugging im Repo. - 2026-05-17 | feat | gateway+frontend-nyla | **RAG-Inventar: echte Chunks + Limit-Transparenz.** Drei Probleme behoben: (1) `routeRagInventory._buildConnectionInventory` zählte bisher `len(FileContentIndex)` (= indizierte Dateien) und labelte das im UI als "Chunks" — bei einer 99-Seiten-PDF erscheint dort statt der echten ~99 Chunks die Zahl 1. Neue `interfaceDbKnowledge.countChunksByFileIds()` macht eine einzige Aggregat-SQL `SELECT "fileId", COUNT(*) FROM "ContentChunk" WHERE "fileId" = ANY(%s) GROUP BY "fileId"` (kein Vector-Body geladen), die Response trägt jetzt `fileCount` UND `chunkCount` pro DataSource + `totalFiles/totalChunks` pro Connection. (2) `RagInventoryPage.tsx` / `connectionApi.ts` zeigen beide Werte getrennt ("25 Dateien · 1240 Chunks") mit Tooltip-Definition für Chunks (~400 Tokens). (3) **Limit-Transparenz**: SharePoint/kDrive/gDrive-Bootstrap stoppen bei den ersten Limits (`MAX_BYTES_DEFAULT=200 MB`, `MAX_ITEMS_DEFAULT=500`, `MAX_DEPTH_DEFAULT=4`, `MAX_FILE_SIZE_DEFAULT=25 MB`); ClickUp analog (`MAX_TASKS_DEFAULT=500`, `MAX_WORKSPACES_DEFAULT=3`, `MAX_LISTS_PER_WORKSPACE_DEFAULT=20`). Bisher: `return` ohne Log + ohne Marker im Bootstrap-Result → User sah "Sync erfolgreich" obwohl 706 Dateien fehlten. Fix: neuer `_recordLimitStop()`-Helper in allen 4 Connectoren setzt `BootstrapResult.stoppedAtLimit` (1. exhausted Budget), schreibt 1 WARNING in den Log und liefert das Feld + die effektiven `limits` im `_finalizeResult` Dict an `BackgroundJob.result`. `routeRagInventory` reicht `lastSuccess.stoppedAtLimit/limits/bytesProcessed` ans Frontend durch. Neuer amber `partialBanner` auf der RagInventoryPage warnt mit "Limit maxBytes=200 MB (200 MB verarbeitet) erreicht — Weitere Dateien wurden NICHT indexiert" und bietet "Erneut indexieren". Verifiziert anhand `local/logs/log_app_20260517.log`: SharePoint-Sync hat genau bei `bytesProcessed=209_894_527 ≥ MAX_BYTES_DEFAULT (209_715_200)` gestoppt (Kumulative Summe der 25 indizierten Dateigrößen = 200.17 MB). ClickUp hat bei `skippedDup=500 >= maxTasks=500` gestoppt. Outlook/Gmail brauchen das gleiche Pattern noch (haben aktuell keine harten Limits im Code, daher kein Bug, aber wenn welche kommen → gleicher Helper). - 2026-05-17 | fix | gateway | **Secrets Decryption TTL-Cache** (`gateway/modules/shared/configuration.py`): `decryptValue()` cached jetzt erfolgreich entschlüsselte Plaintexts process-wide für 60 s (Key = Ciphertext, thread-safe, `clearDecryptionCache()` für Rotation/Tests). Root-Cause aus S7-Smoke-Test (`local/logs/log_app_20260517.log:609`): RAG-Inventory-Polling + paralleler Walker-Burst triggerte für `system`/`DB_PASSWORD_SECRET` >10 Decrypts/s, das Brute-Force-Schutz-Rate-Limit warf `ValueError: Decryption rate limit exceeded` → `routeRagInventory._getInventoryPlatform` HTTP 500. Hot-Path war `mainBackgroundJobService._getDb()`, das pro Call `APP_CONFIG.get("DB_PASSWORD_SECRET")` evaluiert (eager arg eval), bevor `getCachedConnector` überhaupt seinen Wrapper-Cache prüfen kann. Cache-Hit umgeht das Rate-Limit (kein neuer Krypto-Op, nur Re-Read eines bereits autorisierten Plaintexts); Cache-Miss konsumiert weiter Rate-Budget — die Schutzfunktion gegen wiederholt falsche Decrypts bleibt damit erhalten. Wirkt global für alle `_SECRET`-Reader (`auditLogger`, `routeI18n`, alle Feature-Interfaces), nicht nur für den BackgroundJobService. - 2026-05-17 | refactor | gateway | PostgreSQL Connection Pool — Steps S3–S6 abgeschlossen (`c-work/2-build/2026-05-postgres-connection-pool.md`). **S3**: `getCachedConnector` Docstring präzisiert (Cache = Wrapper-Recycling + DB-Init-Spam-Schutz, Pool = echte Connection-Verwaltung). **S4**: Shutdown-Hook `closeAllPools()` in `gateway/app.py` lifespan als letzter Schritt nach Feature-`onStop`-Hooks. **S5**: Neuer Test-File `gateway/tests/unit/connectors/test_connectorDbPostgre_pool.py` mit 6 Concurrency-Tests gegen live-Postgres (auto-skip wenn keine DB erreichbar): 50 Threads × 20 Reads (0 Errors), 20 Threads × 50 Reads (p99 < 5 s), interleaved load/save, `statement_timeout=500ms` triggert `QueryCanceled` und gibt Connection sauber zurück, Pool-Identity pro (host, db, port), `closeAllPools` leert Registry. Beim ersten Lauf entdeckt: psycopg2-Pool wirft `PoolError` sofort bei Exhaustion statt zu blockieren → `borrowConn()` um bounded Wait-Retry erweitert (`_BORROW_WAIT_TIMEOUT_S=30s`, `_BORROW_WAIT_BACKOFF_S=50ms`). Alter `test_connectorDbPostgre_failLoud.py` auf das neue `borrowConn`-Mocking umgestellt (alle 6 weiter grün). **S6**: Regression-Run: 639/656 unit grün (vorher 638) — der eine durch den Refactor verursachte Fail (`test_folder_crud._FakeDb` brauchte `borrowCursor`-Stub) gefixt, die übrigen 17 Failures sind pre-existing RAG/Adapter/Workflow-Drift ohne Pool-Bezug. 76/79 integration grün (3 pre-existing Trustee-Workflow-Fails). Backward-Compat-Stub `borrowCursor` auch in `test_folderRbac._FakeDb` ergänzt. Offen: **S7** (manueller 1 h Smoke-Test, Anleitung in der Plan-Doc) und **S8** (`b-reference/platform/database-architecture.md`). - 2026-05-17 | refactor | gateway | **CORE**: PostgreSQL Connection Pooling implementiert (S1+S2 von `c-work/2-build/2026-05-postgres-connection-pool.md`). Root-Cause: `DatabaseConnector` hielt **eine** psycopg2-Connection pro Instanz und teilte sie via `getCachedConnector(...)` über den gesamten FastAPI-Thread-Pool **und** über asyncio-Tasks. psycopg2-Connections sind NICHT thread-safe — paralleler Zugriff aus z.B. RAG-Polling, `routeI18n`, `mainBackgroundJobService` und Hintergrund-Jobs führte zu `another command in progress` und — viel schlimmer — unendlichem Block in `recv()` (kein `statement_timeout` gesetzt). Das `self._lock` in `DatabaseConnector` war zwar deklariert, aber **nirgends** verwendet. Folge: Backend hat sich komplett aufgehängt, sogar die i18n-API für die Login-Seite lieferte nichts mehr. **Lösung**: Neuer `_PoolRegistry` (`gateway/modules/connectors/connectorDbPostgre.py`) mit `psycopg2.pool.ThreadedConnectionPool` pro `(host, db, port)`, lazy-init, thread-safe (`min=2`, `max=20` per `DB_POOL_MAX_CONN`, `statement_timeout=30s`, `connect_timeout=10s`). Jede DB-Operation borrowed eine Connection via neuem `db.borrowConn()`-Context-Manager (auto-commit/rollback/return); `db.borrowCursor()` als 1:1-Ersatz für das alte `with db.connection.cursor() as cursor:` Pattern. `getCachedConnector` bleibt API-kompatibel, Connector-Wrapper sind nun leichtgewichtig (kein per-instance Socket). Backward-Compat-Shim `db.connection` (no-op `commit()`/`rollback()`/`closed`, RuntimeError auf `cursor()` damit kein Stillschweigend-Bruch). 18 interne Cursor-Stellen + ~30 externe (interfaceRbac, interfaceDbManagement, interfaceDbBilling, interfaceFeatureRealEstate, routeWorkflowDashboard, routeHelpers, gdprDeletion, dbMultiTenantOptimizations, _featureSubAgentTools, scripts/stage0) auf das neue Pattern umgestellt. `_ensure_connection` als No-Op gehalten (Pool re-connectet selbständig). Public Shutdown-Hook `closeAllPools()` wartet auf S4 (FastAPI-Lifespan-Integration). Working-Doc: `c-work/2-build/2026-05-postgres-connection-pool.md` (Step S1+S2). ## 2026-05-16 - 2026-05-16 | fix | frontend-nyla | `AddConnectionWizard` Admin-Consent-Button war NoOp: `ConnectionsPage` hat das `onMsftAdminConsent`-Prop nie übergeben, der `?.()`-Aufruf im Wizard wurde schweigend übersprungen und der Wizard sprang einfach zum nächsten Schritt. Im Backend kam kein einziger Request an `/api/msft/adminconsent` an — User aus Multi-Tenants, in denen die App-Registration noch nie admin-consented wurde, hingen unauflöslich im "Anforderung gesendet"-Screen fest. Fix: `ConnectionsPage._handleMsftAdminConsent` öffnet jetzt ein Popup auf `/api/msft/adminconsent`, und das Prop wird an den Wizard durchgereicht. Doc-Sync: `d-guides/microsoft-entra-registration-checklist.md` (Redirect-URI-Tabelle um `/api/msft/adminconsent/callback` ergänzt — Fehlen führte zu `AADSTS50011`; Multi-Tenant-Hinweis: Admin Consent gilt pro Tenant). ## 2026-05-15 - 2026-05-15 | fix | gateway | `subAiCallLooping.py`: fehlender Top-Level-Import `extractJsonString` (und `repairBrokenJson`) im Modul-Header ergänzt. Bei `getContexts()`-Success (jsonParsingSuccess=True, overlapContext='') flog ein `NameError`, wurde vom zu breiten `except Exception` als "completePart not serializable" geschluckt, hat `mergeFailCount` hochgezählt und nach 3 Iterationen leeren String zurückgegeben — Folge: `subStructureFilling` bekam `""` und meldete `tryParseJson failed: Expecting value` / `No elements produced`. Zusätzlich `except`-Hygiene: `(json.JSONDecodeError, KeyError, TypeError)` → WARNING + retry (erwartete Daten-Probleme), generischer `except Exception` → ERROR mit `exc_info=True` und Re-Raise (Code-Bugs werden nicht mehr 3× silently verfressen). Logging enthält jetzt den Exception-Typ als Prefix. - 2026-05-15 | feat | gateway | Feature Data Sub-Agent Phase 1.5 + Phase 2 abgeschlossen. **Eval-Harness** (`gateway/tests/eval/runTrusteeBenchmark.py`, standalone Runner) fährt 19 Goldstandard-Fragen × 3 Modi (baseline / phase1 / phase2) mit echten AI-Calls gegen einen `FakeFeatureDataProvider` (in-memory `BenchmarkFixture`, kein DB-Setup) und schreibt Markdown + JSON-Report nach `local/notes/`. **Trustee-Ontologie** (`gateway/modules/features/trustee/trusteeOntology.py`): 6 Entities (Account + BankAccount/CashAccount-Spezialisierungen, AccountBalance, JournalEntry/Line), 3 Relations, 6 Constraints (NEVER_AGGREGATE auf alle 4 Balance/Total-Felder, REQUIRES_FILTER_ON für AccountBalance, PREFERRED_TABLE_FOR_INTENT), 8 CanonicalQueryPatterns. **OntologyToPromptCompiler** (`ontologyToPromptCompiler.py`) rendert die Descriptor deterministisch als kompakten Prompt-Block. `mainTrustee.getAgentOntology()` Hook + `_AGENT_DOMAIN_HINTS_LEGACY` geparkt. `featureDataAgent._buildSchemaContext` nutzt `_loadFeatureOntologyBlock()` bevorzugt (Fallback auf `_loadFeatureDomainHints`); `_buildValidatorForFeature()` verkabelt Validator+Ontologie automatisch (Single Source of Truth). Eval-only Override `POWERON_DISABLE_FEATURE_ONTOLOGY=1`. **Side-Fix**: `aggregateTable` exponiert jetzt `filters` im Tool-Schema (war Code-Bug, blockierte SUM-pro-Konto-Anfragen); Validator validiert filters analog zu queryTable. **Resultate** (`local/notes/trustee-benchmark-20260515-161126.md`): Accuracy Baseline 89.5 % → Phase 1 94.7 % (Validator triggert in q13 2× und steuert Agent eigenständig auf queryTable um) → Phase 2 100 % (Ontologie verhindert q09-Halluzination "creditTotal statt closingBalance" präemptiv). +18 neue Unit-Tests in `test_trusteeOntology.py` (Descriptor, Compiler, Validator-Integration, mainTrustee-Hook, _buildValidatorForFeature); `test_featureDataAgent_schema.py` um Ontology-Pfad + Eval-Override erweitert. Working-Doc verschoben nach `c-work/3-validate/2026-05-feature-data-agent-ontology-and-repair.md`. Doc-Sync: `b-reference/gateway/ai-agent.md` (neuer Abschnitt "FeatureDataAgent: Query-Repair-Loop + Ontologie"), `b-reference/gateway/features/trustee.md` (neuer Abschnitt "Agent-Ontologie"), `d-guides/coding-conventions.md` (Migrations-Muster `getAgentDomainHints` → `getAgentOntology`). - 2026-05-15 | feat | gateway | Feature Data Sub-Agent Phase 1 (Query-Repair-Loop) implementiert. Neuer `QueryValidator` (`gateway/modules/serviceCenter/services/serviceAgent/queryValidator.py`) prüft `browseTable`/`queryTable`/`aggregateTable`-Calls **vor** dem Provider gegen das Pydantic-Modell und liefert strukturierte Repair-Hints statt SQL-Exceptions. Vier Constraint-Klassen: `FIELD_NOT_FOUND` (mit Fuzzy-Suggestion via difflib), `OPERATOR_INCOMPATIBLE` (LIKE/ILIKE nur auf String, `>=`/`<=` nur auf Numerisch), `INVALID_AGGREGATE_TARGET` (SUM/AVG auf `*Balance`/`*Total` -- fängt die flagship Trustee-Halluzination `SUM(closingBalance) × 7 Jahre × 13 Perioden ≈ 90× Saldo` deterministisch ab), `ORDER_BY_INVALID`. Neues Datenmodell `datamodelOntology.py` mit `QueryValidationError`, `Constraint`, `OntologyDescriptor` (Phase-2-Ready); Phase-2-Override via Ontologie-Constraints schon eingebaut, aber inaktiv solange keine Ontologie pro Feature definiert ist. `ToolResult.errorDetails: Optional[Dict]` und `ToolCallLog.validationFailureCode: Optional[str]` zu `datamodelAgent.py` ergänzt (LLM bekommt machine-readable Repair-Hint, Audit-Log behält Kurz-String). `agentLoop._computeRepairCounters` aggregiert pro Sub-Agent-Run `validationFailures`, `repairAttempts`, `successAfterRepair` aus den `ToolCallLog`-Einträgen und legt sie auf `AgentTrace` + ins `AGENT_SUMMARY`-Event (Eval-Harness-Ready). Tool-Descriptions der drei Sub-Agent-Tools erklären das `errorDetails`-Format explizit. 35 neue Unit-Tests (24 für QueryValidator, 3 für Tool-Integration, 8 für Trace-Aggregation), alle 62 bestehenden serviceAgent-Tests bleiben grün. Working-Doc: `c-work/2-build/2026-05-feature-data-agent-ontology-and-repair.md`. - 2026-05-15 | chore | wiki | Plan-Finalisierung: (1) RAG C&C Unification+Implementation → status `done`, alle Testplan-Einträge manuell verifiziert, `b-reference/platform/neutralization.md` (DataSource=SSoT, kein neutralizeBeforeEmbed, Tree-Vererbung) und `b-reference/frontend-nyla/architecture.md` (RagInventoryPage, UDB 4. Button, RagRunningBadge, AddConnectionWizard) aktualisiert, beide Pläne nach `4-done/` verschoben. (2) Enterprise Subscription → `4-done/`. (3) FormGenerator Grouping → `4-done/`. (4) STT Benchmark Page implementiert (Backend + Frontend, SysAdmin-only). - 2026-05-15 | docs | wiki | Feature Data Sub-Agent Plan (`c-work/1-plan/2026-05-feature-data-agent-ontology-and-repair.md`) gegen Codebase verifiziert und um 5 Klärungen geschärft: (A) `ToolResult.errorDetails: Optional[Dict]` neu in `datamodelAgent.py` statt JSON-in-error-String; (B) Repair-Telemetrie wandert von `aiAuditLogger` in `AgentTrace`/`ToolCallLog` (per-Run-Aggregation passt nicht in per-Call-Audit-Log); (C) Eval-Fixture als Python-Loader + Pydantic/`recordCreate` statt `fixture.sql` (konsistent mit Trustee-Tests); (D) AC#3 als `repairConversionRate ≥ 0.8` über Repair-Triggered-Subset präzisiert, nicht als End-Accuracy; (E) Doc-Sync-Liste um `b-reference/gateway/features/trustee.md` und `d-guides/coding-conventions.md` erweitert. Plan ist damit umsetzungsreif für Phase 1. ## 2026-05-16 - 2026-05-16 | fix | gateway, frontend-nyla | RAG-Reindex hängt nach manuellem Trigger im Status RUNNING/PENDING, ohne dass ein Walker startet — UI zeigt endlosen Spinner bis Zombie-Killer nach 30 min eingreift. Root Cause: drei Job-Submit-Routen waren sync (`def`, nicht `async def`) und enthielten ein "loop = asyncio.get_event_loop(); ... except RuntimeError: asyncio.run(_enqueue())"-Konstrukt. FastAPI führt sync-Routes im Worker-Threadpool aus → kein laufender Event-Loop → `asyncio.run` öffnet einen temporären Loop, in dem `startJob` `_runJob` via `create_task` registriert → `asyncio.run` schliesst den Loop sofort nach Return → Task wird gecancelled, bevor er ein einziges Statement ausführt. Daily-Resync funktionierte, weil er aus APScheduler im Main-Loop läuft. Fix: alle drei Routen auf `async def` umgestellt und `await startJob(...)` direkt verwendet — keine Loop-Hack-Logik mehr. Betroffene Routes: `routeRagInventory._reindexConnection`, `routeDataSources._updateDataSourceRagIndex` (UDB-Toggle 🧠), `routeDataConnections._updateKnowledgeConsent` (globaler Consent-Toggle). Zusätzlich: Inventar-Response liefert jetzt `lastSuccess` mit `{jobId, finishedAt, indexed, skippedDuplicate, skippedPolicy, failed, durationMs}` plus `lastError.finishedAt` — Frontend zeigt grünes Erfolgs-Banner mit Stats + relativem Zeitstempel ("Sync erfolgreich vor 3 Min — 4 neu indexiert · 183 unverändert (198.3s)") statt nur leerem Spinner; Error wird nur angezeigt wenn er neuer ist als der letzte Erfolg, sonst gewinnt das Success-Banner. `RagInventoryPage` und `RagRunningBadge` pollen jetzt schnell (5s) während Jobs laufen und langsam (60s) im Idle. Badge zeigt einen 4s-"Sync abgeschlossen ✓"-Toast wenn der letzte aktive Job verschwindet. Hängender Job 7ee09ca7 wurde via Onetime-Skript gekillt. ## 2026-05-15 - 2026-05-15 | fix | gateway | RAG-Sync Log-Spam + DB-Reinit-Storm. Root Cause: `mainBackgroundJobService._getDb()` rief bei jeder Job-CRUD-Operation `DatabaseConnector(...)` direkt auf — pro Reindex-Klick mehrfach (`submitJob`, `_updateJob`, `getJob`, `listJobs`, `cancelJob`). Jeder Konstruktor-Call lief komplett durch `_create_database_if_not_exists` → `_create_tables` → neue psycopg2-Connection → `_initializeSystemTable` und loggte "PostgreSQL database system initialized successfully" auf INFO. Folgen: Log-Lärm bei jedem Reindex (~5-10 INFO-Zeilen) + echter DB-Overhead pro Job-Operation. Fix: (1) `_getDb()` nutzt jetzt `getCachedConnector()` (Cache-Key = host/db/port, FIFO-Eviction nach 32, Thread-safe), pro Worker-Lifetime nur noch 1× Init für die jobs-DB. Smoke-Test bestätigt: 3× `_getDb()` → identische Instanz. (2) Init-Log in `connectorDbPostgre.initDbSystem` von INFO auf DEBUG gesenkt + um `db/host/port`-Felder ergänzt — wenn doch mal ein Connector neu erzeugt wird, ist das im Steady-State nicht mehr wert auf INFO-Level zu schreien. Andere Hot-Path-Komponenten (`interfaceDbApp`, `interfaceDbKnowledge`, `aiAuditLogger`) nutzten den Cache bereits korrekt; `auditLogger` ist Singleton, also unkritisch. ## 2026-05-14 - 2026-05-14 | fix | gateway | RAG-Sync: Zombie-Job-Handling + Walker-Timeouts. (1) Neuer 5-Minuten-Cron `background_jobs.zombie_killer` in `mainBackgroundJobService.killZombieJobs()` markiert RUNNING-Jobs ohne Progress-Update >30 min als ERROR und beendet so auf Dauer hängende Bootstraps. (2) Neuer Helper `subWalkerHelpers.py` (`downloadWithTimeout` 60s, `extractWithTimeout` 90s via `asyncio.to_thread`+`wait_for`, `ingestWithTimeout` 60s, `logItemStart`) — alle Walker (sharepoint, kdrive, gdrive, gmail, outlook, clickup) loggen jetzt vor jedem Item `walker.item.start service=… path=…`, sodass das letzte solche Log bei einem Hang das verursachende Item exakt benennt. Sync-Extraktion läuft auf Worker-Thread, blockiert das Event-Loop nicht mehr. (3) Aktive Zombies (msft 81min, infomaniak 81min, clickup 81min) wurden via Onetime-Skript gekillt. - 2026-05-14 | docs | wiki | WW Stephan-Lieferablage: `c-work/2-build/walderwyss-stephan-output/` (Stephan legt nichts in pamocreate ab). Auftrags-PDFs Homepage + Infomaniak für Stephan. - 2026-05-14 | docs | wiki | Lawyer-Feature geplant (Mandatsvorbereitung + Dashboards für Anwaltskanzleien), ausgelöst durch WW-Pitch-Bedarf (David Vasella). Working-Doc in `c-work/1-plan/2026-05-lawyer-feature.md`. ## 2026-05-12 - 2026-05-12 | fix | service-teams-browser-bot | Auth bot PiP loop fix: in full Teams web app, chat toggle navigates to Chat section -> PiP -> periodic scan reopens -> PiP again (endless loop). Fix: added isAuthMode flag to ChatProcedure; in auth mode: skip chat panel toggle entirely, skip periodic reopen, skip panel-open preflight for sendChatMessage. Chat send uses direct input lookup in the meeting view. Replaced _returnToMeetingIfPip() approach (reactive) with prevention (don't toggle) - 2026-05-12 | fix | service-teams-browser-bot | Auth bot meeting minimized (PiP): chat button click hit Teams sidebar "Chats" navigation instead of in-meeting toggle -> meeting minimized, all PeerConnections closed, video/audio broke. Fix: isDangerousNavButton filter excludes sidebar nav, tab-items, submenu triggers; aria-pressed fallback for panel detection in full Teams web app; _openMoreMenu scoped to calling toolbar - 2026-05-12 | feat | gateway, frontend-nyla, teams-bot | Avatar-Bild/Video für TeamsBot: Neues Feld `avatarFileId` in `TeamsbotConfig`/`TeamsbotUserSettings` und `defaultAvatarFileId` in `TeamsbotMeetingModule`. Benutzer können in den Bot-Einstellungen und pro Modul ein Bild oder Video aus den Systemdateien auswählen, das anstelle der statischen Farbfläche als Bot-Video im Meeting gerendert wird. Modul-Einstellung überschreibt Instanz-Default. Gateway löst die Datei beim Session-Start auf, konvertiert zu Base64, und sendet sie im Join-Payload an den Browser-Bot. Bot-seitig: `mediaGetUserMediaPatch.ts` rendert Bilder per `drawImage()` auf den Canvas, Videos per `