wiki/c-work/_CHANGELOG.md
2026-06-08 23:40:35 +02:00

478 lines
199 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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

<!-- status: canonical -->
<!-- lastReviewed: 2026-06-08 -->
# Changelog (c-work)
Eine Zeile pro Change, NEUESTE EINTRAEGE ZUOBERST. Begruendungen gehoeren ins zugehoerige
`c-work/<phase>/<feature>.md` oder die PR-Beschreibung.
Format: `- YYYY-MM-DD | <type> | <scope> | <Kurzbeschreibung> [(c-work: <relPfad>)] [(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 14 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 L0L7**; 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 L1L4. 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 04: 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:<own|shared>]` (`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<T>` (Synthese-Container ohne Flag-Felder); haelt internen `nodeCache: Map<key, UdbBackendNode>` 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 + `<FormGeneratorTree compact selectable={false} allowCreateFolder={false} refreshAfterAction />` + 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|<id>`, `svc|<connId>|<service>`, `ds|<connId>|<sourceType>|<path>`, `mgrp|<mid>`, `feat|<mid>|<code>|<fiId>`, `fdstbl|<fiId>|<table>`) — 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 S3S6 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 `<video>`-Element mit Loop. Für Videos wird FPS auf 15 erhöht und `contentHint='motion'` gesetzt. Fallback bleibt die statische Farbfläche mit Bot-Name.
- 2026-05-12 | fix | gateway, frontend-nyla | TeamsBot Module-Status `[null]`: `createModule`-Route schrieb keinen `status` in die DB, weil `CreateMeetingModuleRequest` kein `status`-Feld hat und `recordCreate` bei Dict-Input keine Pydantic-Defaults anwendet. Frontend `t(null)` ergab `[null]`. Fix: (1) Route setzt `data.setdefault("status", "active")`, (2) `getModules` setzt fehlenden Status defensiv auf `"active"` für existierende Records, (3) Frontend-Fallback `mod.status || 'Aktiv'` als letzte Absicherung.
- 2026-05-12 | fix | frontend-nyla | TeamsBot Director-Panel Sichtbarkeit: gleiches Whitelist-Problem wie zuvor beim Stop-Button. `TeamsbotSessionView.tsx` Z.903 prüfte `['active','joining','pending'].includes(session.status)` — wenn der Status direkt nach Mount kurz leer / unbekannt war (SSE-Race vor `_loadSession`-Resolve, oder Backend schickt einen Übergangsstatus den die Liste nicht kennt), verschwand das ganze "Regieanweisungen"-Panel und kam erst nach Page-Reload wieder. Auf Blacklist umgestellt: `!['ended','error','leaving'].includes(session.status)`. Andere Whitelist-Stellen in derselben Datei (Session-Auswahl Z.136, SSE-Reconnect Z.186/373/439, Listen-Label Z.792) sind defensive Filter mit `status &&`-Guard und bleiben korrekt; Dashboard-Stop-Button war bereits auf Blacklist (Vorgänger-Fix).
- 2026-05-12 | fix | teams-bot, gateway | STT-Sprache: hardcodierten `alternativeLanguages=["en-US"]`-Fallback in `service._processAudioChunk` entfernt. Symptom: nach längerem Bot-TTS-Playback (z.B. 30s Web-Recherche-Antwort) sprang Google STT bei verrauschter Audio auf en-US (`language_code: "en-us"` im Response, Confidence 0.30.5) und lieferte englisches Kauderwelsch ("The transition trade constitute the Range Line."), während saubere Sprache derselben Session korrekt als de-DE mit 0.97 transkribiert wurde. Root Cause: der hardcodierte Englisch-Alternative gab Google die Erlaubnis, bei niedriger DE-Konfidenz nach EN umzuschalten. Fix: nur noch die Sessionsprache (`self.config.language`, Default `de-DE`) wird übergeben — `TeamsbotUserSettings.language` überschreibt `TeamsbotConfig.language` wie gehabt. Settings-Audit bestätigt: alle 12 UI-Felder (`botName`, `aiSystemPrompt`, `responseMode`, `responseChannel`, `transferMode`, `language`, `voiceId`, `triggerIntervalSeconds`, `triggerCooldownSeconds`, `contextWindowSegments`, `debugMode`, `browserBotUrl`) sind im Code aktiv genutzt — keine Karteileichen. (b-ref: teams-bot/architecture.md → "STT auf dem Gateway")
- 2026-05-12 | refactor | teams-bot | Chat-Panel-Detection: tote Fallback-Schichten entfernt. `_isChatPanelOpen()` reduziert auf den nachweislich funktionierenden Pfad: `[data-tid="calling-right-side-panel"]` Existenz+Visibility + chat-spezifischer Child-`data-tid` darin. Schicht 2 (`aria-pressed`-Toggle für "ältere Auth-Layouts") und Schicht 3 (Compose-Box-Sichtbarkeit als Last-Resort) waren toter Code — anon und auth nutzen beide denselben `calling-right-side-panel`-Container, der `aria-pressed`-Pfad war zwei Iterationen vorher selbst als unzuverlässig verworfen worden. Konform mit "Do not add fallback code, if not necessary". (b-ref: teams-bot/architecture.md → "Chat-Panel-Detection")
- 2026-05-12 | feat | teams-bot | Statisches Avatar-Video für anonymen Bot ersetzt Teams' grünen "no video"-Spinner. Neuer Toggle `BOT_USE_CANVAS_VIDEO=true` (Default true im `.env`) plus `BOT_AVATAR_BG_COLOR=#a8d4f0` / `BOT_AVATAR_TEXT_COLOR=#1a3552` in `src/config.ts`, durchgereicht via `audioProcedure.ts` an den Init-Script-Payload `mediaGetUserMediaPatch.ts`. `_startBotAvatarStream()` zeichnet jetzt eine ruhige einfarbige Fläche + zentrierter Anzeigename (kein Pulse, kein Gradient, keine "PORTA"-Marke), Tick-Rate von 15 fps auf 2 fps gesenkt (genug um `captureStream()` in headless Chromium aktiv zu halten, ohne dass der Encoder Frame-Diffs sieht), `contentHint='motion'``'detail'` damit WebRTC für statisches Bild Bitrate spart und Text scharf bleibt. Auth-Bot ist nur betroffen wenn der Toggle aktiv ist. (b-ref: teams-bot/architecture.md → "Statisches Avatar-Video (ersetzt grünen Lade-Spinner)")
- 2026-05-12 | fix | frontend-nyla | TeamsBot Stop-Button Sichtbarkeit: Logik von Whitelist (`['active','joining','pending']`) auf Blacklist (`!['ended','error','leaving']`) umgestellt in `TeamsbotSessionView.tsx` Z.841 + `TeamsbotDashboardView.tsx` Z.264. Verhindert Verschwinden des Buttons bei kurzfristig leerem/unbekanntem Status (z.B. SSE-Race kurz nach Mount, neuer Backend-Status). Button verschwindet nur noch nach explizit terminalen States. Hinweis zur i18n: Source-Keys `t('Sitzung beenden')` / `t('Stoppen')` waren bereits korrekt verpackt — die User-sichtbare englische Anzeige "End Session" stammt aus der Übersetzungs-DB und ist die intendierte i18n-Auflösung des deutschen Source-Keys.
- 2026-05-12 | fix | teams-bot | Chat-Panel-Toggle-Auswahl im Auth-Layout: `chatProcedure._openChatPanel()` neu implementiert. Auth-Full-Teams hat zwei "Chat"-Buttons — einen echten Toggle (UUID-id, `aria="Chat (Ctrl+Shift+2)"`, `aria-pressed="false"`) und einen Schein-Button `#chat-button` ohne `aria-pressed`, der nicht togglt. Vorher: Selector-Liste nahm `#chat-button` als ersten Treffer und klickte 12× denselben falschen Button. Neu: alle visibility-gefilterten Chat-Hint-Kandidaten in Page-Evaluate gesammelt, Toggle-Buttons (mit `aria-pressed`) bevorzugt, geklickte Keys getrackt — derselbe Button wird nie zweimal geklickt, Loop wechselt zum nächsten Kandidaten. Sprachunabhängig (DE/EN-Hints `chat`/`unterhalt`/`besprechung`/`conversation`). (b-ref: teams-bot/architecture.md → "Chat-Panel-Toggle-Auswahl im Auth-Layout")
- 2026-05-12 | fix | teams-bot | Chat-Panel Detection — `vdi-occlusion`-Trapdoor entschärft + Side-Pane-Container als primäre Quelle. Befund am DOM (User-Inspect): die Klasse `vdi-occlusion` ist im neuen Calling-Layout **Permanent-Marker** auf `calling-right-side-panel` und `message-pane-layout` — auch bei sichtbar offenem Panel. Mein vorheriger Check "vdi-occlusion → zu" war daher immer false-negative → Toggle-Loop. Der `<button id="chat-button">` selbst ist byte-identisch in beiden States (kein `aria-pressed`, kein `data-state`, blaue Linie kommt aus CSS-`:has()` auf dem Side-Panel). Detection neu: Schicht 1 prüft `[data-tid="calling-right-side-panel"]` Existenz + offsetWidth/Height + offsetParent (echtes Visibility-Signal), und Mode-Disambiguation per chat-spezifischen Tids drinnen (`message-pane-layout`, `chat-pane-compose-message-footer`, `#chat-pane-list`, `[data-app-name="chats"]` u.a.) — sprachunabhängig, trennt Chat von People/Info ohne Text-Lookup. Schicht 2 (aria-pressed Toggle) bleibt für legacy-Auth-Layouts. (b-ref: teams-bot/architecture.md → "Chat-Panel-Detection — Schicht-Strategie")
- 2026-05-12 | fix | teams-bot | Chat-Panel Detection (zwischenzeitlich) — Schicht-Strategie via aria-pressed: `chatProcedure._isChatPanelOpen()` umgestellt auf "echter Toggle-Button" (chat-Hint im aria-label UND `aria-pressed` ∈ {true,false}, sprachunabhängig) und liest dessen `aria-pressed` als Quelle der Wahrheit — fixt den Auth-Toggle-Loop. Im Auth-Layout existieren mehrere "chat"-aria Buttons (Side-Nav-Einträge "Chats", "Meeting chats", Tab-Items `tab-item-com.microsoft.chattabs.*`), aber nur der echte Meeting-Chat-Toggle hat `aria-pressed`. Vorher prüften wir hartcodiert `#chat-button` (= Side-Nav-App in Auth, NICHT der Toggle), bekamen False-Negative "Panel zu" obwohl offen → periodischer Scan klickte den echten Toggle → schloss Panel → klickte weitere Buttons → Endlos-Toggle-Loop mit `UPR`-Page-Errors. Light-meetings-Pfad (`message-pane-layout` mit `vdi-occlusion`-Klasse oder `offsetHeight=0`) bleibt als Schicht 2 für Anon (kein `aria-pressed`-Toggle vorhanden). (b-ref: teams-bot/architecture.md → "Chat-Panel-Detection — Schicht-Strategie")
- 2026-05-12 | build | teams-bot | Container-Image installiert echtes Chrome: `Dockerfile` ergänzt um `RUN npx playwright install --with-deps chrome` (lädt Google Chrome stable + alle apt-Deps an Playwright-bekannten Pfad) und `ENV BOT_BROWSER_CHANNEL=chrome`. `docker-compose.yml` setzt `BOT_BROWSER_CHANNEL=${BOT_BROWSER_CHANNEL:-chrome}` als Default. Macht den anon-Bot-Fix container-deploy-fähig. (b-ref: teams-bot/architecture.md → "Container-Deployment")
- 2026-05-12 | fix | teams-bot | Anonymer Bot: Browser-Channel auf echtes Chrome umgestellt (`BOT_BROWSER_CHANNEL=chrome`). Playwrights gebündeltes Chromium wird von Teams' `light-meetings`-Pfad als Automation gefingerprintet → erzwungene Lobby + Preheating-Crash (`rejectMediaDescriptionsUpdateAsync`) → "Sorry, we couldn't connect you". Mit lokal installiertem echten Chrome geht der Bot direkt ohne Lobby ins Meeting, kein Crash. Neu: `botBrowserChannel` in `config.ts`, an `chromium.launch({ channel })` in `orchestrator._launchBrowser()` durchgereicht. Bisect bestätigt: weder Media-Wrapper (`BOT_DISABLE_MEDIA_WRAPPERS`) noch Anon-spezifische Chromium-Args / Stealth-Init (`BOT_ANON_USE_AUTH_BROWSER_SETUP`) waren die Ursache; beide Debug-Schalter bleiben als Bisect-Tools im Code. Auth-Joins funktionieren auch mit Bundled-Chromium (echte MS-Session überspringt den Check). (b-ref: teams-bot/architecture.md → "Browser-Channel: echtes Chrome für anonyme Joins")
- 2026-05-12 | fix | teams-bot | TeamsBot Chat-Scraper: Container-Fallback für light-meetings DOM-Branche. **`chatProcedure.ts`** periodischer Scan promoviert `target` zu `document` wenn der Chat-Container (`message-pane-layout`) `offsetHeight=0` hat ODER Selectoren 0 Treffer liefern, aber global `[class*="fui-ChatMessage"]` existiert (light-meetings rendert Chat-Bubbles in paralleler DOM-Branche). Zusätzlich Root-Cause-Guard `if (!text && author !== 'Unknown')` in beiden Pfaden (MutationObserver Strategy 1 + periodischer Scan) restauriert — verhindert dass strukturelle Fragmente ohne Author als `Unknown: 22:04` Transcripts durchsickern. (c-work: 4-done/2026-04-teamsbot-greenfield-ia-and-live-update.md)
- 2026-05-12 | fix | teams-bot | Anonymer Bot: Audio-Capture WebRTC-Wrapper greift nicht mehr in Teams' Pre-Join/Lobby-SDP-Zyklen ein (Ursache des `rejectMediaDescriptionsUpdateAsync`-Crashes und der Lobby-Hänger). `audioCaptureProcedure.ts`: neues `__audioCaptureEnabled`-Flag (default false), `track`-Handler im Wrapper macht nur noch DIAG-Log solange Flag false ist; `__audioCaptureAttachTrack` als window-exponierter Builder; `startCapture()` setzt Flag erst nach `_setState('in_meeting')` und iteriert `getReceivers()` aller `connected` PCs für bereits existierende Audio-Spuren. Label-Filter `mainAudio-*` entfernt (war falsch generalisiert — manche Sessions haben nur eine PC mit UUID-Label). (c-work: 4-done/2026-04-teamsbot-greenfield-ia-and-live-update.md)
- 2026-05-12 | feat | frontend-nyla, gateway | FormGeneratorTable Grouping Refactor: Default auf Inline-Modus umgestellt; Toggle-Icon (Inline/Sections) in TableViewsBar; Sections-Mode Multi-Level (alle Permutationen als flache Sections); pageSizeOptions um 1000/2000/10000 erweitert; Collapse-All/Expand-All Buttons fuer Inline-Grouping; 4 Pages (Connections, Prompts, Files, BillingData) auf Inline-Default umgestellt.
- 2026-05-12 | feat | frontend-nyla | RAG Consent & Control Phase D Ergänzungen: UDB `SourcesTab` 4. Toggle-Button (🧠 ragIndexEnabled) mit Vererbung; `RagRunningBadge` Floating-Komponente (Polling, Job-Dropdown); `WorkspaceRagInsightsPage` physisch gelöscht; `ConnectionsPage` AdminConsent+Infomaniak Standalone-Buttons entfernt (nur noch via Wizard).
- 2026-05-12 | docs | wiki | `b-reference/gateway/ai-agent.md` Abschnitt "RAG Consent & Control" ergänzt (Prinzipien, Datenmodell, Walker-Architektur, API, Frontend-Komponenten, Vererbung); `TOPICS.md` RAG-C&C-Eintrag.
- 2026-05-12 | feat | gateway, frontend-nyla | RAG Consent & Control: Phase AD implementiert. Backend: `DataSource.autoSync``ragIndexEnabled` (rename + Migration-Script); `cancelJob()` + `cancelJobsByConnection()` in BackgroundJobService; `JobProgressCallback.isCancelled()` (3s-cached); Walker-Refactor (alle 5 Connectoren iterieren nur über ragIndexEnabled DataSources + Cancel-Check alle 50 Items + `provenance.dataSourceId`); neuer `subPolicyResolver.py` (Tree-Vererbung); neue Routes `/knowledge-consent`, `/knowledge-preferences`, `/knowledge-stop`, `/datasources/{id}/rag-index`; neuer `routeRagInventory` (4 Endpoints); `GET /connections/` liefert Knowledge-Felder; `WorkspaceRagInsights` Route+Permission gelöscht. Frontend: `AddConnectionWizard` vereinfacht (connector-aware Steps, MSFT AdminConsent + Infomaniak PAT integriert, keine Kostenschätzung/Prefs); `connectionApi.ts` neue Methoden + bereinigte Types; neue `RagInventoryPage` + CSS; `pageRegistry` + `App.tsx` Route + Navigation-Eintrag; `FeatureView` `rag-insights` Mapping entfernt.
- 2026-05-12 | docs | wiki | Implementierungsplan: `1-plan/2026-05-rag-consent-and-control-implementation.md` — code-grounded Phasenplan (A→F) mit Audit-Befunden (`autoSync` ungenutzt → safe rename; `cancelJob`-API fehlt; `routeDataConnections` GET liefert Knowledge-Felder nicht; PATCH-Pattern aus `routeDataSources` als Vorlage), Datei-genaue Änderungen, Acceptance-Gates pro Phase, Risiko-/Decision-Liste, ~17 Tage T-Shirt-Schätzung.
- 2026-05-12 | docs | wiki | Plan: `1-plan/2026-05-rag-consent-and-control-unification.md` — datenzentrierte RAG-Architektur: Per-Element-Index-Toggle in UDB analog Scope/Neutralize (kaskadierende Tree-Vererbung; per-Feld bei Tabellen via `neutralizeFields[]`); Walker iteriert nur über `ragIndexEnabled=true` DataSources; Stop-Mechanismus an drei Sichtbarkeits-Orten (Header-Badge, ConnectionsPage-Row, RagInventoryPage); Wizard ist einziger Eintrittspunkt für alle Connector-Typen (integrierter MSFT Admin-Consent-Step + Infomaniak-PAT-Step); globale `Start > Nutzung > RAG-Inventar` Seite; Wizard ohne Kostenschätzung/Preferences; Löschung der workspace-scoped `WorkspaceRagInsightsPage`.
## 2026-05-11
- 2026-05-11 | feat | gateway | ToolDefinition.displayLabel: neues Feld fuer menschenlesbare Aktivitaetsbeschreibungen; Agent-Loop propagiert Label in TOOL_CALL Events; TeamsBot nutzt es fuer kontextuelle Fortschrittsmeldungen statt "Schritt N von M"
- 2026-05-11 | fix | gateway | TeamsBot Speaker-Attribution: retroaktive Zuordnung von "Unknown"-Eintraegen erfolgt jetzt bei jedem neuen Caption-Speaker, nicht nur beim ersten
- 2026-05-11 | fix | service-teams-browser-bot | Chat-Scraper Root Cause: Timestamp-Separatoren ("22:01") wurden als Chat-Nachrichten erkannt weil der innerText-Fallback auch bei unbekanntem Autor griff; Fallback jetzt nur wenn Autor identifiziert wurde
- 2026-05-11 | fix | gateway | mainServiceWeb progressLogUpdate: fehlende `if operationId:` Guards bei Zeilen 101/125 verursachten "Operation None not found" Warnung wenn Agent-Tool webSearch ohne operationId aufrief
- 2026-05-11 | fix | gateway | SPEECH_TEAMS Prompt: spezifische Beispiele entfernt, Eskalationsregeln generisch gehalten
- 2026-05-11 | fix | service-teams-browser-bot | Anonymous bot stuck after lobby admission: _waitForMeetingAdmission now tracks lobby-to-meeting transition, waits patiently when lobby vanishes (= admitted), and reloads page as recovery; isInMeeting expanded with light-meetings + German selectors
- 2026-05-11 | docs | wiki | d-guides: `google-registration-checklist.md`, `microsoft-entra-registration-checklist.md` (Vendor-Registrierung + Gateway-Env/Code-Referenzen); TOPICS ergänzt.
- 2026-05-11 | feat | gateway, frontend-nyla, wiki | TeamsBot: `GET /api/teamsbot/{instanceId}/dashboard/stream` (SSE dashboardState 3s/20s); dashboard consumes EventSource + reconnect; module tiles navigate to `modules?moduleId=` with expand + scroll highlight; canonical `b-reference/teams-bot/architecture.md`, TOPICS, `b-reference/frontend-nyla/architecture.md` (Teams Bot UI row). (c-work: 2-build/2026-04-teamsbot-greenfield-ia-and-live-update.md)
## 2026-05-10
- 2026-05-10 | feat | gateway | CommCoach Fast Conversational Mode: `AgentConfig.excludeAllTools` flag fuer tool-freie Turns; generischer RAG-Session-Cache in `agentLoop` (TTL 120s / 5 Msgs); CommCoach nutzt beides fuer fluessige Coaching-Gespraeche
- 2026-05-10 | fix | gateway | RAG-Cache-Bug: doppelter `workflowId`-Parameter in `_getOrRefreshRag` behoben (Keyword-Kollision)
- 2026-05-10 | fix | gateway | CommCoach TTS: `_TTS_WORD_LIMIT` von 200 auf 80 Woerter reduziert; Sprachausgabe fasst laengere Antworten jetzt zusammen statt alles vorzulesen
- 2026-05-10 | feat | gateway | `AgentConfig.priority` (speed/quality/cost/balanced) an `agentLoop` durchgereicht; CommCoach conversational turns nutzen `PriorityEnum.SPEED` fuer schnellere Modellauswahl
- 2026-05-10 | fix | gateway | `interfaceDbManagement.getInterface` Singleton Race-Condition behoben: shared mutable instance ueberschrieb featureInstanceId zwischen async Requests; Agent konnte Files nicht finden
- 2026-05-10 | feat | frontend-nyla | TeamsBot UI aligned to greenfield IA: dashboard as KPI + module-activity + quick actions (no duplicate start form); MFA on live session SSE; assistant step adds join mode, MS account block, session context; modules header `Modul anlegen` dialog + `Meeting starten`; nav icons for assistant/modules; `FEATURE_REGISTRY` tab label Dashboard. (c-work: 2-build/2026-04-teamsbot-greenfield-ia-and-live-update.md)
- 2026-05-10 | feat, fix | gateway, frontend-nyla, teams-bot | TeamsBot: `startSession` binds `moduleId` with validation; DB migration M2 `TeamsbotMeetingModule.defaultMeetingLink` / `defaultBotName`; assistant prefill + searchable multi-row module list; modules edit saves defaults; session view polls while pending/joining even with SSE and syncs switcher row; browser bot chat compose marker + safe `AudioContext.close`. (c-work: 2-build/2026-04-teamsbot-greenfield-ia-and-live-update.md)
- 2026-05-10 | docs | wiki | Kanon: `b-reference/gateway/voice-google.md`; TOPICS + README Ordnerbaum; Querverweise in gateway/architecture, teams-bot/architecture, frontend-nyla/architecture, ai-agent.
- 2026-05-10 | feat | gateway, frontend-nyla | STT: configurable model/lightweight/audioFormat on batch `speechToText`; streaming supports model/lightweight/singleUtterance; duration from `result_end_time`; WS `open` + `end_of_single_utterance` replumbing. CommCoach uses `latest_short` + lightweight + single-utterance; Teamsbot passes `audioFormat=linear16`. Chirp/v2 evaluation tracked in `wiki/c-work/1-plan/stt-chirp-v2-evaluation.md`.
- 2026-05-10 | fix | gateway | CommCoach: conversational turns use `operationType=DATA_QUERY` + `priority=SPEED` for faster model selection (gpt-5.4-mini instead of gpt-5.5); model selector speed priority weight scaled from `/10` to `*100` to be meaningful.
- 2026-05-10 | fix | gateway | CommCoach: persona sessions now distinguish first vs follow-up; opening prompt references prior sessions instead of re-introducing persona every time.
- 2026-05-10 | feat | gateway | Document generation: unified `documentTitle` style separate from `headings.h1`; BASE/renderer converts independently; HTML/CSS, PDF title block, Markdown/Text heading offsets updated; DEFAULT_STYLE h1 lowered vs title.
## 2026-05-09
- 2026-05-09 | feat | actan | SanctionsCheck: Login rememberMe + session cookies; public `/signup` + `POST /api/public/signup`; e-mail templates + `templateRenderer`; SMS retries + NOTIFICATION activities; profile tabs; activity API enrichments (`akteurTyp`, `sichtbarkeit`, `includeInternal`) and Activities UI badges/panel.
- 2026-05-09 | feat | gateway | Enterprise Subscription: sysadmin-managed flat-price subscriptions with custom limits (users, features, storage, AI budget), invoice email, hard-block on overage, auto-renewal cron job. New endpoints: POST enterprise/create, enterprise/renew, PUT enterprise/update. (c-work: 1-plan/2026-05-enterprise-subscription.md)
- 2026-05-09 | refactor | frontend-nyla, gateway | CommCoach: remove Dossier view (route, FeatureView registry, RBAC UI object, Dashboard link, pageRegistry icon, barrel export). Session details (summary, score, duration, export) moved into Modules expand view.
## 2026-05-08
- 2026-05-08 | fix | frontend-nyla | CommCoach: add missing `Route path="dossier"` under feature-instance routes (was catch-all not-found).
- 2026-05-08 | fix | gateway | JWT cookies: resolve SameSite/Secure per request via _cookiePolicy() (not import-time); optional APP_COOKIE_SECURE override; env-gateway int/prod: APP_COOKIE_SECURE=true + CORS nyla*.poweron-center.net.
- 2026-05-08 | fix | gateway | JWT cookies: SameSite=None+Secure when APP_API_URL is HTTPS (cross-origin SPA+API); SameSite=Lax on HTTP localhost. Fixes credentialed API calls when UI and gateway differ by site. CSRF middleware unchanged.
- 2026-05-08 | chore | frontend-nyla | config/.env.{dev,int,prod}: keep only VITE_API_BASE_URL and VITE_APP_NAME; removed unused flags and duplicated Entra/secret keys (backend owns secrets). env.d.ts aligned.
- 2026-05-08 | refactor | frontend-nyla | Remove MSAL from UI: deleted authConfig.ts + AuthProvider.tsx, rewrote ProtectedRoute (sessionStorage-only), removed useMsalRegister, simplified logout, uninstalled @azure/msal-browser + @azure/msal-react. All auth logic lives in gateway.
- 2026-05-08 | chore | frontend-nyla | Rename env files: `.env.{dev,int,prod}``env-poweron-nyla-{dev,int,prod}.env` (naming matches workflow). Updated workflows, .gitignore, README.
- 2026-05-08 | chore | gateway | Rename env files: `env_{dev,int,prod,prod_forgejo}.env``env-gateway-{dev,int,prod,prod-forgejo}.env`. Updated GitHub/Forgejo workflows, deploy-gcp, .gitignore, .dockerignore, .gcloudignore, Dockerfile, scripts.
- 2026-05-08 | chore | gateway | Domain migration poweron-center.net → poweron.swiss: APP_API_URL, CORS origins, OAuth redirect URIs (MSFT, Google, ClickUp) in env-gateway-{int,prod,dev}. Updated billing email and doc URLs in stripeCheckout.py, datamodelTeamsbot.py.
## 2026-05-07
- 2026-05-07 | fix | gateway | sandboxExecutor: `open` durch In-Memory-VirtualFS ersetzt (statt geblockt); AI-generierter Code kann jetzt Dateien lesen/schreiben ohne echten Dateisystem-Zugriff
- 2026-05-07 | feat | gateway | RedmineTicketMirror + RedmineTicketDto: Feld `doneRatio` (% erledigt) ergaenzt; Sync-Mapping und DTO-Konvertierung aktualisiert
- 2026-05-07 | feat | gateway | Neues Agent-Tool `redmine.listRelations` fuer direkte Abfrage der Beziehungstabelle (Filter: issueId, relationType, pagination)
- 2026-05-07 | feat | gateway | `redmine.listTickets`: `offset`-Parameter ergaenzt fuer echte Pagination; Response liefert `offset` + `hasMore` statt `truncated`
## 2026-05-06
- 2026-05-06 | fix | frontend | UDB tab "Chatverläufe" renamed to "Dossiers" (UnifiedDataBar.tsx + i18n seed for xx/de/en/fr)
- 2026-05-06 | fix | gateway | PDF rendering: (1) _convertUnifiedStyleToInternal now maps spaceBeforePt/spaceAfterPt for headings, lineSpacing for paragraphs, bulletChar for lists — previously dropped silently; (2) DEFAULT_STYLE heading spacing increased (h1: 24pt before, h2: 20pt) for professional visual hierarchy; (3) _renderJsonBulletList uses dedicated BulletItem ParagraphStyle with leftIndent/firstLineIndent from bullet_list config — was identical to paragraph normalStyle
## 2026-05-05
- 2026-05-05 | fix | frontend-nyla | FormGeneratorTree: `window.confirm()` durch `useConfirm`-Modal ersetzt; Hover-Icons ueberlappen jetzt die Dateigroesse statt daneben zu stehen (spart Breite im File-Tree)
- 2026-05-05 | fix | gateway | 4 warning fixes: (1) mainServiceAgent skips getWorkflow for synthetic workflowIds with ":" prefix (commcoach billing keys); (2) ActionNodeExecutor + actionToolAdapter inject parentOperationId so ai.process progress nests correctly; (3) ChatService.interfaceDbComponent now receives featureInstanceId — root cause was missing scope causing getFile to miss featureInstance-scoped files during graph execution; (4) IMAGE_GENERATE sections use concise _buildImagePrompt instead of full 700KB userPrompt — eliminates DALL-E truncation
- 2026-05-05 | fix | gateway | _getOrCreateTempFolder rewritten to use ComponentObjects.getOwnFolderTree/createFolder instead of raw db.getRecordset bypass; added explicit logging for all code paths; _persistLargeDocument now logs updateFields before updateFile call
- 2026-05-05 | fix | frontend+gateway | AI-generated files not visible: (1) useCommcoach.ts sendMessage documentCreated handler used undefined `sessionId` instead of `session.id` causing ReferenceError silently swallowed by SSE parser catch-all — system note never shown; (2) SSE parser catch blocks narrowed to JSON.parse only so handler errors propagate; (3) _getOrCreateTempFolder re-implemented to find/create user Temp folder; (4) all file-update call sites consolidated into single updateFile call to avoid RBAC scope conflict
- 2026-05-05 | fix | gateway | CommCoach completeSession: session.get("contextId") → session.get("moduleId") — stale field name caused sessionCount to never update (always 0); getModules now computes live sessionCount from sessions table
- 2026-05-05 | fix | gateway | actionToolAdapter: _persistLargeDocument now handles bytes documentData (PDF/DOCX/PPTX were silently discarded); _formatActionResult returns sideEvents for fileCreated; scope set to featureInstance when featureInstanceId available; CommCoach+TeamsBot _runAgent forward FILE_CREATED as documentCreated SSE; silent warnings elevated to errors for data-loss scenarios
## 2026-05-04
- 2026-05-04 | feat | * | **CommCoach Persona-Management: Gespraechspartner konfigurierbar.** Backend: 5 neue Builtin-Personas (Paartherapeutin, Psychologe, Rechtsanwalt, Mediatorin, HR-Managerin), ModulePersonaMapping M:N-Tabelle mit DB-Migration M8, GET/PUT Module-Persona-Zuordnung. Frontend: Settings-Tab 'Gespraechspartner' mit FormGeneratorTable CRUD (Create/Edit/Delete custom Personas, Inline-Toggle isActive), Modul-Edit-Dialog mit Persona-Multi-Select, Session-Persona-Picker filtert nach Modul-Zuordnung. (c-work: `2-build/2026-04-comcoach-greenfield-ia.md`)
## 2026-05-03
- 2026-05-03 | feat | * | **ComCoach + TeamsBot Greenfield-IA: Implementierung abgeschlossen.** Backend: CoachingContext -> TrainingModule (Rename, moduleType Enum, kpiTargets, category entfernt), neue Module-CRUD-Routes. TeamsBot: TeamsbotMeetingModule Entity (additiv), Module-CRUD, agentRun SSE-Events sichtbar, Stats-Karten, adaptives Dashboard-Polling (3s/30s). Frontend: Beide Features 5-Tab-IA (Dashboard, Assistent, Module, Session, Settings), neue View-Komponenten (AssistantView Wizard, ModulesView CRUD), KeepAlive nur noch fuer Session-Tab, Settings Statistik entfernt, Dashboard navigiert zu Modules statt Dossier. (c-work: `2-build/2026-04-comcoach-greenfield-ia.md`, `2-build/2026-04-teamsbot-greenfield-ia-and-live-update.md`)
- 2026-05-03 | docs | wiki | **ComCoach + TeamsBot Greenfield-IA: Plaene verifiziert und in `2-build/` verschoben.** Alle Zeilennummern und Code-Referenzen gegen aktuelle Codebase geprueft (alle korrekt). ComCoach-Plan ergaenzt: `goals`-Feld existiert bereits (JSON->Freitext-Migration), `category`-Enum wird durch `moduleType` ersetzt, `description`-Feld bleibt. TeamsBot-Plan ergaenzt: Namenskonvention `startedByUserId` dokumentiert. Beide Plaene: `<!-- status: build -->`, `<!-- verified: 2026-05-03 -->`. (c-work: `2-build/2026-04-comcoach-greenfield-ia.md`, `2-build/2026-04-teamsbot-greenfield-ia-and-live-update.md`)
- 2026-05-03 | fix | gateway | **AI Step Output: `context` Feld zeigte Prompt statt Input-Context.** `actionNodeExecutor.py` baute `out["context"]` falsch aus `promptText + extractedContext` zusammen. Wenn AI ein Binary-Dokument erzeugte (z.B. Excel), war `extractedContext` leer und `context` wurde identisch mit `prompt`. Jetzt wird der tatsaechliche aufgeloeste `context`-Input-Parameter gespeichert.
- 2026-05-03 | fix | frontend-nyla | **FormGeneratorTree: Auto-Scroll bei Expand am unteren Bildschirmrand.** Wenn ein Gruppenobjekt in der unteren Haelfte des sichtbaren Bereichs expanded wird, scrollt der Container den Parent-Node zur Mitte, damit Kinder ohne manuelles Scrollen sichtbar sind.
- 2026-05-03 | fix | frontend-nyla | **Automation > Workspace Tab: Polling fuer laufende Runs.** `_WorkspaceTab` pollt `fetchWorkspaceRunDetail` alle 3s solange Run-Status nicht terminal. Stoppt automatisch bei completed/failed/cancelled.
- 2026-05-03 | fix | gateway | **executeCode Sandbox: `type` entblockt, `io` restricted, Modul-Liste dynamisch.** `type()` aus `_PYTHON_BLOCKED_BUILTINS` entfernt. `import io` liefert eingeschraenktes Modul (nur StringIO/BytesIO, kein io.open). Tool-Beschreibung dynamisch aus `SANDBOX_ALLOWED_MODULES` generiert. `type()` war faelschlich in `_PYTHON_BLOCKED_BUILTINS` (harmlos ohne exec/eval). `StringIO`/`BytesIO` als sichere Builtins (io.open wird nicht exponiert). Tool-Beschreibung aktualisiert.
- 2026-05-03 | fix | gateway | **AI Agent: docItem-Resolution Root Cause + executeCode readFile.** (1) `coerceDocumentReferenceList`: Dict-Pfad strippt jetzt `docItem:`/`docList:` Prefix korrekt via `from_string_list` (war: Prefix blieb in `documentId`, erzeugte Doppel-Prefix bei `to_string()`). (2) `process.py`: `DocumentItemReference` wird nur im Automation2-Kontext (kein Chat-Workflow) an `_resolve_file_refs_to_content_parts` geroutet; im Agent-Kontext fliessen sie korrekt durch `getChatDocumentsFromDocumentList` (war: alle Refs wurden abgefangen und fehlerhaft als File-Store-IDs behandelt). Gebrochenen `_resolveChatDocIdToFileId`-Fallback entfernt. (3) executeCode Sandbox: `readFile(fileId)` Built-in fuer Workspace-Dateizugriff (kein open(), nur managed Files via interfaceDbComponent).
- 2026-05-03 | fix | gateway | **AI Agent + Rendering: 9 Issues geloest.** (1) Sandbox `time`-Modul erlaubt. (2) ActionToolAdapter: grosse Docs als Workspace-Files persistiert. (3) downloadFromDataSource: Debug-Logging fuer Filename-Swap. (4) clickup_listTasks: Datums-/Custom-Field-Filter. (5) Renderer style-Bug: `style` Keyword auf allen 8 Renderern akzeptiert (CSV, Text, JSON, Markdown, Image, CodeXml, CodeJson, CodeCsv). (6) _ServicesAdapter: workflow-Setter fuer Agent-Kontext (getChatDocumentsFromDocumentList braucht workflow). (7) _ServicesAdapter: interfaceDbComponent Property (ai_process file-ref Resolution). (8) Auto-index nach Neutralize: mandateId/featureInstanceId an _autoIndexFile durchgereicht. (9) Kombination: Agent sollte AI-Workspace-Aufgaben effizient loesen.
- 2026-05-03 | chore | frontend-nyla | **Stage 4: FolderTree Dead-Code geloescht + Tree-Assessment.** Gesamter `components/FolderTree/`-Ordner entfernt (FolderTree.tsx 1170 LOC, SharepointBrowseTree.tsx 319 LOC, actions/, CSS -- alles Dead Code). ESLint-Deprecation-Regel entfernt. Assessment aller verbleibenden Tree-Kandidaten: DataPicker, InstanceHierarchyView, AccessRulesEditor, TreeNavigation, redmineTreeLogic -- alle SKIP (kein Entity-Tree-Paradigma). (c-work: `4-done/2026-05-formgenerator-tree-and-folder-recovery.md`)
- 2026-05-03 | docs | wiki | **Stage 5: FormGeneratorTree Wiki-Dokumentation.** `formgenerator.md`: neuer Abschnitt FormGeneratorTree (Provider-Interface, Features, Props, Verwendung, Tree-vs-Table-Vergleich). `architecture.md`: FolderTree-Referenzen durch FormGeneratorTree ersetzt. `TOPICS.md`: FormGenerator-Eintrag ergaenzt. Plan nach `4-done/` verschoben. (c-work: `4-done/2026-05-formgenerator-tree-and-folder-recovery.md`)
- 2026-05-03 | docs | wiki | **FormGeneratorTree Plan: Stage 3 deferred, Stage 0-2 abgeschlossen.** Stage 3.A/3.B (SourcesTab/ChatsTab-Refactor auf FormGeneratorTree) deferred -- domaen-spezifische Tree-Logik passt nicht sinnvoll auf generische TreeNodeProvider-Abstraktion. Plan-Status auf `done` gesetzt. Stage 2.C Checkliste aktualisiert mit allen 2.C-Fixes (405-Fix, typeMap, Refresh-Icon, confirm-Dialoge, FilesPage-Sync). (c-work: `4-done/2026-05-formgenerator-tree-and-folder-recovery.md`)
## 2026-05-02
- 2026-05-02 | fix | frontend-nyla | **Tree batch-delete: separate Ordner/Dateien-Aktionen.** `TreeBatchAction.typeFilter` in `types.ts`; `FormGeneratorTree.tsx` filtert `selectedIds` nach Typ und zeigt Counts pro Button. `FolderFileProvider.tsx` liefert zwei getrennte Batch-Aktionen (Ordner-Cascade-Delete vs. File-Delete/Batch-Delete). **FilesPage tree-table Sync wiederhergestellt:** `selectedFolderId` filtert Tabelle nach Ordner; File-Klick im Tree scrollt+highlighted die Tabellenzeile (2.5s auto-clear). (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`)
## 2026-05-01
- 2026-05-01 | feat | gateway | Stage 1.B FormGenerator Folder RBAC: FileFolder in TABLE_NAMESPACE, 11 folder methods in interfaceDbManagement (CRUD, move, cascade-delete, scope, neutralize), 6 routes in routeDataFiles replacing 501 stubs
- 2026-05-01 | feat | frontend-nyla | **Stage 2.A + 2.B: FilesTab + FilesPage tree integration.** `FilesTab.tsx` rewritten from `FormGeneratorTable` to two `FormGeneratorTree` sections (Eigene/Geteilt mit mir) with drag-drop upload, refresh-by-key, and workflow-import on node click. `FilesPage.tsx` updated with split-view layout: 300px tree panel (left, own+shared trees) and `FormGeneratorTable` (right) with view-mode toggle (Tree-Sicht / Liste mit Gruppen); groupingConfig applied only in grouped mode; upload and refresh sync both tree and table. (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`)
- 2026-05-01 | feat | gateway | **FormGeneratorTree Plan Stage 0 + 1.A:** Schema-Check-Skript `scripts/stage0_filefolder_schema_check.py`; `FileFolder` Pydantic + `FileItem.folderId` in `datamodelFiles.py`; Stub `getOwnFolderTree`/`getSharedFolderTree`; Routen `GET /api/files/folders/tree` (leere Liste) + Folder-Mutationen 501 bis Stage 1.B; `migrate_folders_to_groups.py` nach `modules/migrations/_archive/` mit README. (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`)
- 2026-05-01 | docs | wiki | **Plan FormGeneratorTree: Stages packetisiert nach LLM.** Stages 1-3 in Sub-Packets aufgeteilt (1.A BE-Boilerplate / 1.B BE RBAC-Kern / 1.C FE Greenfield / 2.A FE-Boilerplate / 2.B FE-Komposition / 2.C manuelle Verifikation / 3.A Provider-Refactors / 3.B Tab-Umbau). Pro Packet: Modell-Hinweis (Opus 4.6 fuer Architektur/RBAC/Greenfield, Composer 2 Fast fuer Boilerplate/Recovery/Doku), Eingang/Lieferumfang/Abnahme. Eskalations-Regel ergaenzt. (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`)
- 2026-05-01 | docs | wiki | **Plan FormGeneratorTree + Folder-Recovery konsolidiert.** Neuer FormGenerator-Familienteil `FormGeneratorTree` (Provider-Pattern, Cascade-Multiselect, DnD `application/x-poweron-tree-items`, Backend-Attribut-Resolution) zur Konsolidierung der ~8 parallelen Tree-Implementierungen. Recovery der Folder/File-Funktionen ueber **Modell A** (`FileFolder` + `FileItem.folderId` reaktivieren; DB-Schema unveraendert). RBAC-/Sichtbarkeitsmodell verbindlich: Default-Scope `personal`; Sharing nur ueber Owner-Scope-Patch; geteilte Objekte read-only fuer Nicht-Owner (analog `updateFile`/`deleteFile`); UI rendert in `FilesTab`/`FilesPage` **zwei getrennte Trees** (Eigene/CRUD + Geteilt mit mir/read-only); Files ohne sichtbaren Folder-Kontext fallen in den Root mit `contextOrphan`-Hint; Folder-Scope-Cascade auf Files = opt-in; Folder-Neutralize-Vererbung beim Drop = nein; Drag aus Geteilt in Eigenes = gesperrt. 22 konsolidierte Entscheidungen (D1-D22), keine offenen Backlog-Punkte mehr. UDB hat fix drei Tabs (Files, Sources, Chats) auf `FormGeneratorTree`. `FormGeneratorTable.groupingConfig` bleibt unveraendert. Kein `FormGeneratorChart` -- Berichte = **`FormGeneratorReport`** (Komponenten-Zeile in `b-reference/frontend-nyla/formgenerator.md`). 6 Stages (0 Schema-Sanity-Check optional, 1 Backend+Skeleton, 2 FilesTab/FilesPage Recovery, 3 SourcesTab/ChatsTab Konsolidierung, 4 optional weitere Trees, 5 Doku). (c-work: `1-plan/2026-05-formgenerator-tree-and-folder-recovery.md`)
## 2026-05-01
- 2026-05-01 | docs | wiki | **Plan D (AI Reports Pipeline) abgeschlossen -> `4-done/`.** Alle Checklisten-Items auf `[x]`, Testplan-Status auf `done`, Header `status: done`, `completed: 2026-05-01`. Wiki-Referenz-Updates (`ai-agent.md`, `workflow.md`, `neutralization.md`) als TODO fuer naechste Review-Runde markiert. (c-work: `4-done/2026-04-ai-reports-theming-and-pipeline.md`)
## 2026-04-30
- 2026-04-30 | refactor | gateway | **GraphEditor: Hidden DataRefs sichtbar gemacht fuer alle `ai.*` Nodes.** `frontendType: "hidden"` -> `"dataRef"` fuer `documentList` in `ai.prompt`, `ai.summarizeDocument`, `ai.translateDocument`, `ai.convertDocument` ([gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py](../../gateway/modules/features/graphicalEditor/nodeDefinitions/ai.py)). Zwei neue sichtbare Parameter auf `ai.prompt`: `context` (`dataRef`, beliebiger Upstream-Output, wird vom Action zu JSON serialisiert) + `documentTheme` (`select`, finance/legal/...). Action-Definition `ai.process` ([gateway/modules/workflows/methods/methodAi/methodAi.py](../../gateway/modules/workflows/methods/methodAi/methodAi.py)) deklariert `context` und `documentTheme` jetzt explizit, statt sie als undeklarierte Pass-Through-Keys mitlaufen zu lassen. Effekt: Trustee-Templates (Budget-Vergleich, KPI, Cashflow, Forecast, Year-end) zeigen ihre bisher unsichtbaren DataRef-Bindings (`trigger.payload.documentList`, `refresh.data.accountingData`) jetzt im Properties-Panel als gruene Bindungs-Boxen, der User kann sie via DataPicker veraendern. Kein Frontend-Code-Change noetig: bestehender `DataRefRenderer` + `DataPicker` rendern die JSON-DataRef-Werte direkt. Runtime unveraendert -- `resolveParameterReferences` verarbeitet `{"type":"ref",...}` wie bisher.
## 2026-04-29
- 2026-04-29 | test | gateway | Phase 7 AI-Reports: 19 unit tests for MD-to-JSON parser (inlineRuns), styleDefaults resolver, AiCallOptions allowedModels whitelist, inline-image paragraphs
- 2026-04-29 | feat | gateway, frontend-nyla | **D: AI Reports Pipeline komplett umgebaut.** Phase 1: MD->JSON konsolidiert (Inline-Run-Modell, `_parseInlineRuns`). Phase 2: Generisches Style-System (`styleDefaults.py`, `resolveStyle`, `renderDocument` style-Param). Phase 3: Alle 5 Renderer auf Style-Lookups + Inline-Runs umgestellt. Phase 4: Inline-Bilder in Paragraph/Tabelle/Liste. Phase 5: `allowedModels` in `AiCallOptions` + `_calculateEffectiveModels` im AI-Gate. Phase 5a: `WorkspaceUserSettings` Persistenz (GET/PUT Endpoint). Phase 5b: `modelMultiSelect` im FlowEditor + Workspace-Settings UI. Phase 6: Trustee-Templates mit `requireNeutralization` + Finance-Style-Hint. Phase 7: 19 Unit-Tests. (c-work: `1-plan/2026-04-ai-reports-theming-and-pipeline.md`)
- 2026-04-29 | feat | gateway, frontend-nyla | **B: Trustee Budget-Vergleich auf Excel umgestellt.** `mainTrustee.py` Template: `resultType: "xlsx"` + `documentTheme: "finance"` hart gesetzt. Prompt komplett refaktoriert: 1 Tabelle alle Konten, 1 Uebersichts-Chart (kein pro-Konto-Chart), Management-Summary. Frontend: Hinweistext "Excel-Bericht" im Budget-Tab ergaenzt. XLSX-Renderer (PNG-unter-Tabelle) verifiziert. (c-work: `3-validate/2026-04-trustee-budget-comparison-refactor.md`)
- 2026-04-29 | feat | gateway, frontend-nyla | **A2 Workflow-Run-Workspace + targetFeatureInstanceId implementiert.** Phase 1: `AutoWorkflow.targetFeatureInstanceId` (Pydantic + DB), createWorkflow-Fallback, Save-Validation (400 fuer non-template ohne targetId), Execute-RBAC-Check, `executeGraph` Placeholder-Substitution (`{{featureInstanceId}}`), Scheduler-Durchreichung, `_copyTemplateWorkflows` explizites Setzen, idempotente Boot-Backfill-Migration. Phase 2: FlowEditor CanvasHeader "Ziel-Instanz" Dropdown mit FeatureStore-Integration, Save/Load mit `targetFeatureInstanceId`. Phase 3: Neuer Backend-Endpoint `GET /api/automations/runs` + `GET /api/automations/runs/{runId}/detail` (RBAC via FeatureAccess), neuer "Workspace" Tab in AutomationsDashboardPage mit Run-Liste + Detail-View (Steps, Files, Download). Phase 4: TrusteeAnalyseView inline-Results durch "Im Workspace ansehen"-Link ersetzt, TrusteeAbschlussView Workspace-Link nach Completion ergaenzt. (c-work: `1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md`)
- 2026-04-29 | docs | wiki | **Plan A2 finalisiert: Trustee Workflow-Audit + Generischer Workflow-Run-Workspace.** Cross-Check gegen Codebase (12 Punkte verifiziert, 4 Korrekturen eingearbeitet): `executeGraph` hat keine Placeholder-Substitution (muss neu gebaut werden), `_copyTemplateWorkflows` setzt `featureInstanceId` nicht im Payload (implizit via GE-Interface), `TrusteeAbschlussView` hat kein `resultText`/`resultDocuments` (nur status/summary), 7 Trustee-Templates statt 5 im Audit. Risiko-Sektion ergaenzt. (c-work: `1-plan/2026-04-trustee-workflow-audit-and-run-workspace.md`)
- 2026-04-29 | chore | gateway | **`gateway/scripts/` aufgeraeumt: 5 obsolete one-shot-Scripts archiviert + 1 Ad-hoc-Snippet geloescht.** Im Anschluss an den Bootstrap-Cleanup. Neuer Unter-Ordner `gateway/scripts/_archive/` mit eigenem README beschreibt Inhalt + Begruendung pro Datei. Verschoben (mit User-Bestaetigung pro File): `check_orphan_featureinstance.py` (hardcoded Vor-Ort-UUIDs), `script_db_cleanup_duplicate_roles.py` (`IS NULL`-Bug-Cleanup, Bug laengst gefixt), `migrate_async_to_sync.py` (one-shot `async def` -> `def` Codemod, Refactor durch), `i18n_rekey_plaintext_keys.py` (Frontend Klartext-Keys, Migration durch siehe `4-done/2026-04-ui-i18n-dynamic-language-sets.md`), `script_db_migrate_accessrules_objectkeys.py` (Navigation-API-Migration MIGRATION_MAP nur fuer trustee+realestate hardcoded, durch). Geloescht: `_listMandates.py` (26-Zeilen Ad-hoc-Debug-Snippet, jederzeit aus Git rekonstruierbar). Status danach: 21 aktive Scripts in `gateway/scripts/` (inkl. neues `script_db_audit_legacy_state.py`) + 5 archivierte Scripts. Dev-Boot 20:24:09 bestaetigt das aufgeraeumte interfaceBootstrap.py + Telemetrie-Hook laufen sauber durch (kein WARN, kein Restbestand, neue Code-Pfad-Line-Refs). (c-work: `4-done/2026-04-bootstrap-migrations-cleanup.md`)
- 2026-04-29 | refactor | gateway | **Bootstrap-Cleanup ausgefuehrt: 4 idempotente Migrations-Routinen + 1 Aggregations-Fallback aus dem Boot-Pfad entfernt.** Vor Removal Audit-Skript `gateway/scripts/script_db_audit_legacy_state.py` (NEU, lese-only, exit-code-gated) gegen Dev-DB gelaufen -> 4/5 GREEN sofort, Check 5 (RAG-Orphans) RED mit 20 verwaisten `FileContentIndex`-Rows ohne `mandateId`/`featureInstanceId` -> via `--purge-rag-orphans` bereinigt -> Re-Audit 5/5 GREEN. Dann entfernt aus `gateway/modules/interfaces/interfaceBootstrap.py`: `_migrateMandateDescriptionToLabel` (Funktion + Aufruf), `_migrateMandateNameLabelSlugRules` (Funktion + Aufruf, ~64 Zeilen), `initRootMandate`-Legacy-Block (`name="Root"`-Migration, 7 Zeilen; Funktion selbst bleibt), `_migrateAndDropSysAdminRole` (Funktion + Aufruf, ~95 Zeilen). In `interfaceDbKnowledge.py`: `aggregateMandateRagTotalBytes`-Fallback-Block (`try`/`except` mit FileItem-ID-Korrelation aus Management-DB, ~27 Zeilen) entfernt -- die Funktion bleibt aktiv, da sie 4 externe Caller hat. Ersatz: neuer Helper `gateway/modules/interfaces/_legacyMigrationTelemetry.py` mit 4 lese-only WARN-Checks (gleiche Logik wie Audit-Skript, prozessweit gecached) wird am Ende von `initBootstrap` einmalig aufgerufen -- falls je doch Restbestand auftauchen sollte (alter DB-Restore o.ae.), gibt's klare WARN-Logs mit Routine-Name + IDs. Tests `tests/unit/bootstrap/test_mandateNameMigration.py` (8 Tests) und `tests/unit/rbac/test_sysadmin_migration.py` (5 Tests) geloescht (referenzierten entfernte Funktionen, wuerden ImportError werfen). Smoke-Test: 17/17 verbliebene rbac+bootstrap-Tests GREEN, Imports aller drei Module GREEN. User-Statement zur Risk-Lage: "die codebase lief bereits auf int und main/prod" -- d.h. die idempotenten Migrations sind dort schon mehrfach durchgelaufen, das Removal-Risiko = 0; das Audit-Skript bleibt fuer pre-deploy-Gating. Plan urspruenglich in `1-plan/`, jetzt direkt in `4-done/2026-04-bootstrap-migrations-cleanup.md`. (c-work: `4-done/2026-04-bootstrap-migrations-cleanup.md`)
- 2026-04-29 | feat | frontend-nyla, gateway | **Generischer `frontendType: templateTextarea`** fuer Freitext mit `{{nodeId.path}}`-Variablen (DataPicker-Insert); `email.draftEmail.context` und `ai.prompt.aiPrompt` nutzen ihn statt reiner Textarea -- Aufloesung ausschliesslich via bestehendes `resolveParameterReferences` (kein Loop-Spezialcode im Executor). Loop-Preview-Enrichment + IfElse/AI-`responseData`-Picker bleiben. Unit-Test `test_legacy_string_template_loop_current_item_nested` in `test_automation2_graphUtils.py`.
- 2026-04-29 | docs | wiki | **6 neue UX-/Architektur-Plaene angelegt** in `wiki/c-work/1-plan/` aus Topics-TODO A-F: (A1+A2) Trustee-Workflow-Audit (alle 5 Reporting-Workflows GREEN auf Pick-not-Push) + generischer `WorkflowRunWorkspace`-Tab unter `/automations` als Single-Source-of-Truth fuer Workflow-Resultate, plus Pflicht-Binding `Workflow.featureInstanceId` (`2026-04-trustee-workflow-audit-and-run-workspace.md`); (B) Budget-Vergleich auf Excel-only mit eindeutigem 1-Tabelle-1-Chart-Prompt, Word-Pfad raus (`2026-04-trustee-budget-comparison-refactor.md`); (C) Inventar idempotenter Boot-Routinen, Removal-Plan fuer 5 b-Kandidaten via Audit-Skript + 30-Tage-Telemetrie (`2026-04-bootstrap-migrations-cleanup.md` -- noch am gleichen Tag umgesetzt, siehe Eintrag oben, jetzt unter `4-done/`); (D) Theme-System fuer AI-Reports pro Workflow (4 Themes standard/finance/marketing/presentation + Mandate-CI-Overlay), MD->JSON-Konsolidierung, Renderer-Refactor, Bilder-in-Tabellenzellen (`2026-04-ai-reports-theming-and-pipeline.md`); (E) ComCoach Greenfield-IA mit `TrainingModule`-Datenmodell (Rename `CoachingContext`) + 5-Tab-Struktur Dashboard/Assistent/Module/Session/Einstellungen, dossier=coaching-Doppelung weg, `?context=`-Bug fix (`2026-04-comcoach-greenfield-ia.md`); (F) TeamsBot Greenfield-IA analog mit neuer `MeetingModule`-Entity + 5-Tab-Struktur und 4 Live-Update-Fixes (SSE-Reconnect-Hook, agentRun-Status-Bubble, stats-Karten, adaptives Dashboard-Polling) (`2026-04-teamsbot-greenfield-ia-and-live-update.md`). Empfohlene Reihenfolge der Bauphase: C -> A2-Phase-1 (FeatureInstance-Pflicht) -> A2-Phase-2/3 (Workspace) -> B -> D -> E/F parallel. (c-work: 6 neue Dateien in `1-plan/`)
- 2026-04-29 | fix | gateway | **`checkForDuplicateFile` macht jetzt einen RBAC-Cross-Check und liefert keine "Geister-Duplicate" mehr aus.** Ursache des `File with ID ... not found`-Fehlers in `downloadFromDataSource` direkt nach `Duplicate detected for user ...`: `interfaceDbComponent` wird ueber `serviceHub` ohne `featureInstanceId` initialisiert, `checkForDuplicateFile` faellt deshalb auf den `mandateId`-Filter zurueck und benutzte `db.getRecordset` (kein RBAC) -- damit konnte er ein File aus einer FREMDEN featureInstance returnen. Der nachfolgende `updateFile`-Aufruf prallte dann am RBAC-gefilterten `getFile` ab und crashte den ganzen Tool-Call. Sauberer Fix an der Wurzel: nach dem Recordset-Treffer zwingend `self.getFile(fileId)` als RBAC-Cross-Check; wenn `None`, gilt als kein Duplicate fuer den aktuellen Scope und der Caller (`saveUploadedFile`/`createFile`) erstellt eine frische per-Scope-Kopie. Damit entfaellt jeglicher Workaround in `updateFile` (die in der Vorgaenger-Session gebaute Try/Except-Schleife wurde gestern bereits ausgebaut, weil sie in einer Sackgasse stand). Folge: identische Files koennen pro Mandate mehrfach existieren -- eines pro featureInstance -- was gewollt ist, da sie pro Scope eigene Folder-/Tag-/Neutralize-Metadaten brauchen. Symptom-Doku in `wiki/b-reference/gateway/agent-file-bridge.md` (Section "Ghost-Duplicate-Fix"). (c-work: Begleitfix zum Agent-File-Bridge-Build vom selben Tag)
- 2026-04-29 | fix | gateway | **Agent-Tools binden jetzt jede produzierte Datei als ChatDocument an den aktiven Workflow -- damit funktioniert der `documentList`-Resolver fuer Agent-Outputs.** Bisher erzeugten `downloadFromDataSource`, `writeFile mode=create`, `renderDocument`, `generateImage` und `createChart` zwar eine `FileItem` (via `saveUploadedFile`/`saveGeneratedFile`), liessen den Workflow-Document-Graph aber unberuehrt -- statt eines `ChatDocument` floss nur ein `sideEvent fileCreated` an die UI. Folge: `getChatDocumentsFromDocumentList` (und damit `ai_summarizeDocument` / `ai_process` / `context_extractContent` / `context_neutralizeData`) konnte die FileItem-IDs nicht aufloesen, weil der Resolver ausschliesslich `workflow.messages[*].documents[*].id` matcht. Symptom war "Building structure prompt with 0 valid ContentParts" und entsprechend leere Summaries direkt nach einem Agent-Download. Loesung: zentraler Helper `_attachFileAsChatDocument(services, fileItem, label, userMessage)` in `coreTools/_helpers.py`, der intern eine ChatMessage (Rolle `assistant`, Status `step`, generierter `documentsLabel`) inklusive einem ChatDocument (mit `roundNumber`/`taskNumber`/`actionNumber` aus dem aktiven Workflow) via `chatService.storeMessageWithDocuments` persistiert -- exakt das Pattern, das `workflowProcessor.persistTaskResult` und `methodTrustee.extractFromFiles` schon laenger benutzen. Helper wird jetzt aus allen fuenf File-erzeugenden Agent-Tools aufgerufen; der ToolResult-Text traegt einheitlich beide IDs (`documentList ref: docItem:<chatDocId>` fuer AI-Tools, `file id: <fileId>` fuer `readFile`/Embeds). Tool-Descriptions + `conversationManager.buildSystemPrompt` ergaenzt um die explizite Anweisung "use docItem:<id> in documentList, NOT the file id, NOT a `{"documents":[...]}` wrapper". Zusatzlich: `mainServiceAgent.runAgent` propagiert das Workflow-Object jetzt in alle Service-Contexts (`chat._context.workflow` etc.) -- bisher tat das nur `workflowManager._propagateWorkflowToContext`, weshalb der Agent ueber die Workspace-Route ohne aktiven Workflow-Context lief und `chatService._workflow` `None` war. Folge: der Helper konnte ohne diesen Propagations-Fix nie greifen. Die in der vorhergehenden Session eingebaute RBAC-tolerante `updateFile`-Try/Except-Schleife in `_downloadFromDataSource` ist jetzt obsolet und wieder ausgebaut -- mit korrektem ChatDocument-Bind kommt der `featureInstanceId` ueber den Workflow-Pfad und die Duplicate-File-RBAC-Probleme verschwinden an der Wurzel. Pattern-Doc neu unter `wiki/b-reference/gateway/agent-file-bridge.md`. (c-work: kein dedizierter Eintrag, kritischer Fix on top der documentList-Coercer-Aenderung)
- 2026-04-29 | fix | gateway | **`ai_process` / `context_extractContent` / `context_neutralizeData` akzeptieren jetzt LLM-typische Dict-Wrapper-Formate fuer `documentList`.** Der Agent serialisiert `documentList` regelmaessig als `{"documents":[{"id":"<uuid>","name":"<file>"}]}` (Folge der `type=DocumentList`-Schema-Definition, das LLMs als generic Object lesen). Bisher fielen alle drei Actions in den `else: Invalid documentList type: <class 'dict'>`-Branch und liefen mit leerer Document-Liste weiter -- der Folder-Summarize-Workflow produzierte dann eine Zusammenfassung "ueber 0 Dokumente". Loesung: zentraler Helper `coerceDocumentReferenceList(value)` in `datamodelDocref.py` der ALLE praktisch vorkommenden Shapes parst -- `None`, `str`, `DocumentReferenceList`, `list[str]`, `list[dict mit id/documentId/label]`, `dict mit "documents"/"references"/"items"/"files"-Wrapper`, und `dict mit id/documentId` (single item). Alle vier Aufrufstellen (process.py + extractContent.py + neutralizeData.py x2) auf den Helper umgezogen; die Inline-`ActionDocument`-Sonderbehandlung in `process.py` bleibt davor. Helper wirft NIE -- gibt im worst case eine leere Liste mit WARN-Log zurueck, der Caller entscheidet ob das fatal ist. (c-work: kein dedizierter Eintrag, Begleitfix zum Infomaniak-Test)
- 2026-04-29 | fix | gateway | **`listFiles`-Tool: dict-vs-attribute-Mismatch in `mainServiceChat.listFiles` behoben.** `interfaceDbComponent.getAllFiles()` returnt seit dem Pydantic-Refactor `List[dict]` (jeder Eintrag ist ein `FileItem.model_dump()` mit Label-Spalten), aber `mainServiceChat.listFiles()` griff weiter mit `fileItem.fileName` / `fileItem.id` zu (Pydantic-Style) -- daher `'dict' object has no attribute 'fileName'` und `'dict' object has no attribute 'id'` aus dem `listFiles`-Agent-Tool. Komplette Methode auf `.get(...)` umgestellt; Type-Annotation in `getAllFiles` (`Union[List[FileItem], PaginatedResult]`) ist immer noch irrefuehrend -- das ist ein orthogonaler Tech-Debt-Punkt, kein Blocker. (c-work: kein dedizierter Eintrag)
- 2026-04-29 | fix | gateway | **kDrive: Single-File-DataSources funktionieren jetzt sauber (Browse + Download).** Wenn der User eine einzelne Datei als DataSource anhaengt (z.B. `path=/2980592/12`, wo `12` die kDrive-File-ID einer `.html`-Datei ist), brachten browseDataSource und downloadFromDataSource bisher Folgefehler: (1) Browse rief blind `/2/drive/{driveId}/files/{fileId}/files` auf -> kDrive antwortet `400 destination_not_a_directory` -> Adapter loggte das als Empty-Folder -> Agent dachte "leerer Ordner" und konstruierte einen Pseudo-Pfad `/2980592/12/platform-overview.html`. (2) `download(/2980592/12/platform-overview.html)` nahm dann `segments[-1] = "platform-overview.html"` als File-ID -> `/2/drive/2980592/files/platform-overview.html` -> `404 method_not_found`. Saubere Loesung: (a) Neuer Helper `_lastNumericSegment(segments)` zieht aus einem Pfad immer die letzte rein-numerische ID -- kDrive-IDs sind grundsaetzlich Integer; haengt der Agent einen Filename dran, wird dieser ignoriert. (b) `KdriveAdapter.browse` holt vor dem Children-Listing die Item-Metadata (`/files/{id}`); bei `type=file` wird ein Single-Entry mit Name/Size/MimeType/Modified zurueckgegeben statt children aufzurufen. (c) `KdriveAdapter.download` benutzt denselben Helper + denselben Metadata-Fetch (jetzt extrahiert in `_fetchItemMeta`). Folge: Single-File-Quellen sind jetzt browse-/downloadbar; Folder-Quellen verhalten sich unveraendert. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-29 | fix | gateway | **downloadFromDataSource: Metadata-Updates auf Duplicate-Files werfen den Tool-Call nicht mehr aus dem Tritt.** `saveUploadedFile` returnt bei Hash+Name-Match das EXISTIERENDE FileItem -- das kann aber in einem anderen `featureInstanceId`/Folder-Scope leben als die aktuelle Anfrage. Die nachfolgenden `updateFile`-Aufrufe (`featureInstanceId`/`neutralize`/`folderId`) prallten dann am RBAC-gefilterten `getFile` ab und warfen `File with ID ... not found` -- obwohl der Download korrekt war (File ist in der DB, addressierbar via `fileItem.id`). Fix: die drei Metadata-Updates laufen jetzt in einer Schleife mit individuellem Try/Except und WARN-Log; der Tool-Call meldet weiter Success mit der File-ID. (c-work: kein dedizierter Eintrag, Folge des kDrive-Fixes oben)
- 2026-04-29 | fix | gateway | **Agent-Tools: `kdriveFolder`/`calendarFolder`/`contactFolder` werden jetzt korrekt auf die Connector-Services `kdrive`/`calendar`/`contact` gemappt.** Der `SourcesTab.tsx` schreibt diese FE-seitigen Source-Type-Literals beim Anhaengen einer Datenquelle ans `DataSource`-Record; `_dataSourceTools._resolveDataSource` (und parallel `routeFeatureWorkspace._SOURCE_TYPE_TO_SERVICE`) hatten aber nur Eintraege fuer SharePoint/OneDrive/Drive/Outlook/Gmail/FTP/ClickUp -- daher kamen `Service 'kdriveFolder' not available. Options: ['kdrive', 'calendar', 'contact']` aus `browseDataSource`/`searchDataSource`/`downloadFromDataSource` sobald der Agent eine Infomaniak/Calendar/Contact-Quelle anfasste. Beide Maps um die drei Eintraege erweitert; `DataSource.sourceType`-Field-Description ergaenzt; Inline-Kommentar im `_dataSourceTools.py` warnt vor Drift gegenueber FE und `_SERVICE_MAP`. Bestehende `DataSource`-Records funktionieren ohne Migration (das `sourceType`-Literal bleibt gleich). (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-29 | fix | gateway | **kDrive-Download folgt jetzt dem 302-Redirect.** Der Endpoint `/2/drive/{driveId}/files/{fileId}/download` antwortet bandwidth-bedingt mit `302 Found -> presigned CDN URL` (Standard fuer File-Bytes); unser `_infomaniakDownload`-Helper hatte aber `allow_redirects=False` (kopiert vom `_infomaniakGet`-Helper, wo das wichtig ist um nicht versehentlich auf OAuth-Login-Pages zu landen). Folge: jeder kDrive-Download lieferte `None` -> `Tool result: downloadFromDataSource FAILED -> Download returned empty` im Agent-Log. Fix: nur fuer Downloads `allow_redirects=True`, mit Doc-String der erklaert warum (gleiches Pattern bei Calendar/Contacts-Export-Endpoints, gleicher Host = Authorization-Header bleibt erhalten). Listing/Metadata-Calls bleiben weiter `allow_redirects=False`, damit nicht-PAT-faehige Routes als `302` sichtbar bleiben statt eine HTML-Login-Page zu liefern. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-29 | fix | gateway | **Anthropic-Plugin: `temperature` wird fuer Extended-Thinking-Modelle (Claude 4.7 Opus, spaeter 4.7 Sonnet/Haiku, alle 5.x) nicht mehr gesendet.** Anthropic antwortet seit dem 4.7-Release mit `400 invalid_request_error: 'temperature' is deprecated for this model.` -- nur der modellinterne Default ist akzeptiert. Analog zum gestrigen `_supportsCustomTemperature`-Helper im OpenAI-Plugin (gpt-5/o-Serie) jetzt auch hier: Praefix-Match auf `claude-opus-4-7`/`-sonnet-4-7`/`-haiku-4-7` und `claude-*-5*` -> `temperature` weglassen. Wirkt in allen drei Anthropic-Pfaden (`callAiBasic`, `callAiBasicStream`, `callAiImage`). Aeltere Modelle (Claude 4.5/4.6) bekommen `temperature` weiter wie gehabt. (c-work: kein dedizierter c-work-Eintrag -- analog OpenAI gpt-5-Fix vom 2026-04-28)
- 2026-04-29 | fix | gateway, frontend-nyla, wiki | **Infomaniak-Connector: kDrive findet jetzt auch Drives, in denen der User nur `role: user` ist (statt `account_admin`).** Live-Beweis vom User mit Files in `https://ksuite.infomaniak.com/1696919/kdrive/app/drive/2980592/files/11`: das Drive haengt korrekt an account_id 1696919, aber `/2/drive?account_id=1696919` antwortet trotzdem `200 [] -- empty`, weil dieser Endpoint zur Drive-Manager-Admin-Sicht gehoert (filtered auf `account_admin: true`) und nicht zur Endbenutzer-Sicht. Direkt-Aufrufe wie `/2/drive/2980592/files` funktionieren fuer denselben User mit `role: user` einwandfrei (PDF "Start_with_kDrive.pdf", Ordner "Nyla-Analysen" und "Onboarding" alle sichtbar). Root-Endpoint gefunden: `GET /2/drive/init?with=drives` -- enumeriert ALLE Drives die der User sehen kann, unabhaengig von der Admin-Rolle, braucht NUR den `drive`-Scope (verifiziert mit PAT der `accounts`-Scope explizit nicht hat). Implementierung: (a) Neuer Helper `listAccessibleDrives(token) -> List[dict]` im `connectorInfomaniak.py` -- ein einzelner `/2/drive/init?with=drives`-Call, raised `InfomaniakIdentityError` mit Scope-Message wenn `drive`-Scope fehlt. (b) `KdriveAdapter` haelt jetzt `_drives: Optional[List[Dict]]` (statt `_accountId`/`_accountIds`); `_listDrives()` mappt direkt aus dem gecachten Init-Response. (c) `submit_infomaniak_token` validiert in zwei Schritten: `listAccessibleDrives` (`drive`-Scope) und `resolveOwnerIdentity` (`workspace:calendar`/`workspace:contact`-Scope). (d) Der zwischenzeitliche `resolveAccessibleAccountIds()` + `accounts`-Scope-Workaround wieder ENTFERNT (war eine Sackgasse: `/1/accounts` listet Manager-Organizations, nicht Drive-Accounts). (e) `_probeDriveScope()`-httpx-Helper im Submit-Route entfernt; `httpx`-Import + `INFOMANIAK_API_BASE`-Konstante raus. Frontend: PAT-Setup-Modal wieder auf 4 Pflicht-Scopes zurueck (`accounts` raus). Doku: Status-Tabelle, Validation-Section und "How the kDrive adapter discovers your drives" komplett auf den `init`-Endpoint umgeschrieben. **Bestehende Connections sollten ohne User-Aktion sofort funktionieren -- es ist kein neuer Scope und keine Token-Rotation noetig, der Adapter wechselt nur den Discovery-Endpoint.** (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-29 | fix | gateway | **Infomaniak-Connector: Calendar-Events und Contacts-Download auf die echten PAT-faehigen Pfade gezogen.** Live-Tests gegen die Vendor-API zeigten zwei Pfad-Mismatches im gestrigen Build: (1) Calendar-Events: der nested Route `/api/pim/calendar/{id}/event` 302t zur OAuth-Login-Seite (also nicht PAT-faehig); korrekter Endpoint ist `/api/pim/event?calendar_id={id}&from=YYYY-MM-DD HH:MM:SS&to=...` mit Pflicht-Range max 3 Monate (Vendor-Constraint `range_must_be_lower_than_3_months`) -- Adapter waehlt jetzt fix 90-Tage-Window (heute -30 / +60), URL-Encoding via `urllib.parse.quote`. Event-Detail und `.ics`-Export laufen ueber `/api/pim/event/{eventId}` und `.../export` (also ohne calendar-Praefix). (2) Contacts-Download: `/addressbook/{book}/contact/{id}` antwortet mit `500 unexpected_error` und `.../export` 302t zu OAuth -- beide Detail-Endpoints sind also nicht PAT-faehig. Listing dagegen funktioniert, liefert aber per Default nur Stammdaten -- ohne `with=emails,phones,addresses,details` kommen Email/Phone/Adresse leer. Loesung: ContactAdapter holt das Listing mit dem `with`-Param und rendert die `.vcf` selbst via `_renderInfomaniakVcard()` (vCard 3.0, escaped, mit N/FN/ORG/EMAIL/TEL/ADR/URL/NOTE) -- konsistent mit MSFT/Google-Adapter, die ihre `.vcf`s ebenfalls selbst synthetisieren. Plus: Helper `_safeFileName` aus dem Calendar-Adapter zu modullokal hochgezogen, von beiden Adaptern genutzt; ungenutzte `import re`/`import json`-Inline-Imports raus. **kDrive-Adapter ist im Live-Test korrekt:** `/2/drive?account_id=1696919` antwortet 200 mit leerem Array fuer den Test-Account (kein kDrive-Produkt aktiviert). (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
## 2026-04-28
- 2026-04-28 | refactor | gateway | **Infomaniak-Connector: `account_id` ist Adapter-State, nicht Connection-State.** Symptom im Log: `Infomaniak GET https://api.infomaniak.com/2/drive?account_id=pat-XXXXXXXX -> 422 validation_rule_integer "The account id must be an integer."`. Root cause: der `KdriveAdapter` las `connection.externalId` als `account_id`-Quelle, und bei kaputten Submits konnte dort der Token-Fingerprint ("pat-59ee48d9") statt der `account_id` stehen. Saubere Loesung statt Fingerprint-Migration: (a) Neuer modullokaler Helper `resolveOwnerIdentity(token) -> InfomaniakOwnerIdentity` im `connectorInfomaniak.py` -- versucht PIM Calendar (`workspace:calendar`-Scope), faellt auf PIM Contacts (`workspace:contact`-Scope) zurueck, raised `InfomaniakIdentityError` wenn keine Owner-Records gefunden. (b) `KdriveAdapter` hat keinen `accountId`-Konstruktor-Parameter mehr, sondern resolvt zur Laufzeit ueber `_ensureAccountId()` (gecached auf der Adapter-Instanz). Damit ist der Adapter self-contained und liest **nichts** mehr aus der Connection -- `externalId` ist reiner UI-State. (c) `submit_infomaniak_token` ruft denselben `resolveOwnerIdentity()` als Pre-Flight-Check, dann `/2/drive?account_id={resolved}` als sauberer 200-Probe. Der frueher noetige `_probeScope`-422-Tolerance-Hack ist entfallen. (d) `getServiceAdapter` hat keine kDrive-Sonderbehandlung mehr; alle drei Adapter werden uniform mit `accessToken` konstruiert. (e) `_PIM_PREFIX` ist auf Modul-Ebene definiert; CalendarAdapter und ContactAdapter haben keine Klassen-Konstanten mehr. **Konsequenz: jede existierende Infomaniak-Connection arbeitet ohne User-Aktion sofort wieder, auch wenn `externalId` historisch einen Fingerprint enthaelt -- der Adapter zieht die `account_id` deterministisch aus der API.** Setup-Guide um Architektur-Abschnitt erweitert. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-28 | feat | gateway, frontend-nyla | **MSFT- und Google-Connector: CalendarAdapter + ContactsAdapter neu, plus Reconnect-Button.** Nachdem Infomaniak heute Calendar/Contacts kann, ziehen MSFT und Google nach. Backend: (a) `oauthProviderConfig.googleDataScopes` um `calendar.readonly` + `contacts.readonly` erweitert, `msftDataScopes` um `Calendars.Read` + `Contacts.Read`. (b) `connectorMsft.CalendarAdapter` (Graph: `me/calendars`, `me/calendars/{id}/events?$top&$orderby=start/dateTime desc`, Pagination via `@odata.nextLink`, `.ics`-Download als selbstgebautes RFC-5545 VCALENDAR/VEVENT da Graph keinen `$value`-Endpoint fuer Events kennt; `$search` fuer query). (c) `connectorMsft.ContactsAdapter` (Graph: `me/contactFolders` + virtueller `default`-Ordner fuer `me/contacts`, `me/contactFolders/{id}/contacts?$orderby=displayName`, `.vcf` als vCard-3.0 selbstgebaut mit N/FN/ORG/TITLE/EMAIL/TEL/ADR/NOTE). Helper `_eventToIcs`, `_contactToVcard`, `_safeFileName`, `_personLabel` plus `_icsEscape`/`_icsDateTime`. (d) `connectorGoogle.CalendarAdapter` (Calendar v3: `users/me/calendarList`, `calendars/{id}/events?singleEvents=true&orderBy=startTime`, `.ics` aus Event-Detail). (e) `connectorGoogle.ContactsAdapter` (People API: `contactGroups` + virtueller `all`-Folder ueber `people/me/connections`, fuer Gruppen `people:batchGet?resourceNames=...`, Search via `people:searchContacts`, `.vcf` aus `personFields=names,emailAddresses,phoneNumbers,organizations,addresses,biographies`). (f) `_SERVICE_MAP` von beiden Connectoren um `"calendar"`/`"contact"` ergaenzt; `routeFeatureWorkspace` und `routeFeatureGraphicalEditor` Service-Label-/Icon-Maps um `kdrive`, `calendar`, `contact` ergaenzt, damit die UDB sinnvolle Anzeigen macht. (g) **Reconnect-Flow**: `POST /api/connections/{id}/connect` akzeptiert optionalen Body `{"reauth": true}` und haengt `&reauth=1` an die Auth-URL. `routeSecurityMsft.auth_connect` setzt bei `reauth=1` `prompt=consent` (sonst select_account/login), `routeSecurityGoogle.auth_connect` droppt bei `reauth=1` `include_granted_scopes=true` damit Google strikt fuer die aktuelle Scope-Liste neu signiert (sonst werden neue Scopes still uebersprungen). Frontend: `connectionApi.connectService(id, reauth?)` -> POST mit Body, `useConnections.connectWithPopup(id, reauth?)` reicht durch, `ConnectionsPage` zeigt fuer aktive MSFT/Google/ClickUp-Connections einen `FaSyncAlt`-Button "Erneut verbinden (neue Scopes erteilen)" mit eigenem Loading-Set. Bestehende Connections muessen einmal reconnected werden, damit Calendar/Contacts in der UDB auftauchen. (c-work: `c-work/2-build/2026-04-msft-google-calendar-contacts.md`)
- 2026-04-28 | fix | gateway | **OpenAI-Connector: `temperature` fuer GPT-5.x / o-Serie aus dem Payload nehmen.** Symptom im Log: jede AI-Anfrage failt mit HTTP 400 `Unsupported value: 'temperature' does not support 0.2 with this model. Only the default (1) value is supported.`, der Failover spricht 14 Modelle durch und schreibt `Recorded failure for gpt-5.5, cooldown 60.0s`. Root cause: die GPT-5-Familie (gpt-5, gpt-5.4*, gpt-5.5*) und die o-Serie (o1/o3/o4) sind Reasoning-Modelle; OpenAI akzeptiert dort -- analog zur `max_tokens` -> `max_completion_tokens`-Restriction -- nur den Default (`1`). Wir senden aber unverhandelt `temperature=0.2` aus jedem `AiModel`-Eintrag. Fix: Helper `_supportsCustomTemperature(modelName)` und in `callAiBasic`/`callAiBasicStream`/`callAiImage` den Key nur conditional ins Payload aufnehmen (Modellnamen mit Praefix `gpt-5`, `o1`, `o3`, `o4` lassen ihn weg). Der vom User im UI gesetzte Override ueber `AiCallOptions.temperature` wird auf den nicht unterstuetzten Modellen still verworfen statt einen 400 zu erzwingen. Tests: `tests/unit/aicore/test_aicorePluginOpenai_temperature.py` (18 Tests, parametrisiert ueber Legacy-Modelle vs. Reasoning-Familie). Suite gesamt: 530 passed.
- 2026-04-28 | docs | wiki | **Infomaniak-Connector: Mail-Adapter formal als "blocked by vendor" markiert.** Erschoepfende Pfad-Tests am 2026-04-28 zeigen: alle 7 plausiblen Mail-Endpoints sind heute nicht PAT-faehig. `api.infomaniak.com/{1,2}/mail` -> 404 nginx (existiert nicht); `mail.infomaniak.com/api/mail[?account_id=...]`, `/api/pim/mail`, `/api/pim/mailbox`, `/api/pim/folder` -> 302 zu `login.infomaniak.com/authorize` (nur OAuth-Web-Session, Bearer-PAT wird abgelehnt); `mail.infomaniak.com/api/mail/?account_id=...` -> 301 zu `http://mail.infomaniak.com:5000` (interner Cyrus-IMAP-Port, von aussen nicht erreichbar). Der `workspace:mail`-Scope bleibt im PAT-Standard-Setup, damit der MailAdapter spaeter ohne Token-Rotation freigeschaltet werden kann; Setup-Guide und c-work-Doku zementieren den Befund. Kein Code-Change. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-28 | feat | gateway, frontend-nyla | **Infomaniak-Connector: ContactAdapter neu.** Nachfolge-Tests gegen die Contacts-PIM-API zeigten, dass `https://contacts.infomaniak.com/api/pim/addressbook` (Singular!) mit dem PAT-Scope `workspace:contact` und Bearer-Auth `200` + JSON liefert -- gleiche Antwort-Struktur wie Calendar (`addressbooks[].id/name/account_id/...`). Die Plural- und `/contact*`-Pfade (`/api/pim/contacts`, `/api/pim/contact/addressbook`) sind weiterhin OAuth-only bzw. 404. Neuer `ContactAdapter` 1:1 analog `CalendarAdapter`: `browse("/")` -> Adressbuecher, `browse("/{bookId}")` -> Kontakte (Display-Name, Email, Phone, Organization in Metadata), `download("/{bookId}/{contactId}")` -> `.vcf` via `/contact/{id}/export` mit JSON-Fallback, `search()` als kostenguenstiger Client-Filter. Geteilte Organisations-Adressbuecher (`name=""`, `is_dynamic_organisation_member_directory=true`) bekommen "Organisation" als Anzeigename, sonst waere der Tree-Knoten leer. Neue Konstante `_CONTACTS_BASE`, `ContactAdapter` in `_SERVICE_MAP` registriert. Frontend: `SourcesTab` kennt `contact`-Icon (👤), -Color und Mapping; PAT-Modal nennt Contacts als heute aktiv (Mail bleibt "in Vorbereitung"). Setup-Guide: Status-Tabelle, UDB-Verifikations-Liste und Validation-Beschreibung aktualisiert. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-28 | feat | gateway, frontend-nyla | **Infomaniak-Connector: kDrive PAT-Fix + Calendar-Adapter neu.** Nach den ersten echten PAT-Test-Calls aufgeraeumt: (a) `/2/drive` braucht ein `account_id`-Query-Arg, sonst `422 account_id required`. Fix: `account_id` einmalig beim Token-Submit aus dem Calendar-PIM-Endpoint (`https://calendar.infomaniak.com/api/pim/calendar` -- liefert `account_id`, `user_id`, Anzeigename) ziehen, in `UserConnection.externalId` persistieren und ueber `InfomaniakConnector.getServiceAdapter` als Konstruktor-Parameter in den `KdriveAdapter` injizieren. `/1/profile` (haette `user_info`-Scope verlangt) und `/1/mail` (existiert nicht: 404 nginx) raus aus den Probes. (b) `_probeScope` toleriert jetzt 4xx ausser 401/403 (z.B. 422 `validation_failed`) als "Scope ist da, Endpoint braucht nur weitere Args". (c) Neuer `CalendarAdapter` (`browse` -> Calendars/Events ueber `calendar.infomaniak.com/api/pim/calendar`, `download` -> `.ics` via `/event/{id}/export`, JSON-Fallback). `_infomaniakGet` und `_infomaniakDownload` akzeptieren ein optionales `baseUrl`, sodass Calendar gegen `calendar.infomaniak.com` und kDrive gegen `api.infomaniak.com` laufen koennen. (d) `MailAdapter` aus `_SERVICE_MAP` entfernt: `mail.infomaniak.com/api/mail` redirected mit 302 zu `login.infomaniak.com/authorize`, akzeptiert also keine PATs; gleiches gilt fuer `contacts.infomaniak.com/api/pim/contact`. Beide Scopes werden weiterhin in `grantedScopes` gespeichert, damit kuenftige Adapter ohne Token-Rotation aufgeschaltet werden koennen. (e) Frontend: `SourcesTab` kennt `calendar`-Icon, -Color und `_SERVICE_TO_SOURCE_TYPE`-Mapping; PAT-Modal in `ConnectionsPage` zeigt Calendar als zweiten aktiven Service, Mail/Contact als "in Vorbereitung, Scope schon mitnehmen". (f) Setup-Guide aktualisiert (Status-Tabelle + Validation-Beschreibung). (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-28 | refactor | gateway, frontend-nyla | **Infomaniak-Connector: OAuth -> Personal Access Token.** Infomaniaks `login.infomaniak.com/authorize` akzeptiert nur Identity-Scopes (`openid`, `profile`, `email`, `phone`); Versuche mit `kdrive`/`mail`-Scopes quittieren mit `error=invalid_scope`. Datenzugriff geht ausschliesslich ueber manuell im Manager erstellte PATs. Umbau: (a) Backend `routeSecurityInfomaniak` komplett neu -- ein Endpoint `POST /api/infomaniak/connections/{id}/token`, validiert PAT via `GET https://api.infomaniak.com/1/profile`, persistiert Bearer mit 10-Jahres-Horizont (analog ClickUp), entfernt OAuth-Connect/Callback-Pfade. (b) `tokenManager.refreshInfomaniakToken` + `tokenRefreshService._refresh_infomaniak_token` entfernt, AuthAuthority.INFOMANIAK aus den Background-Refresh-Filtern raus -- PATs sind langlebig. (c) `oauthProviderConfig.infomaniakDataScopes` + `infomaniakDataScopesForRefresh` entfernt. (d) `routeDataConnections.connect_service` antwortet bei Infomaniak jetzt mit 400 + Hinweis auf den PAT-Endpoint. (e) Env-Cleanup: `Service_INFOMANIAK_DATA_CLIENT_ID/SECRET` und `Service_INFOMANIAK_OAUTH_REDIRECT_URI` aus `.env` + `env_dev/int/prod[_forgejo].env` raus. (f) Frontend: `useConnections.createInfomaniakConnectionAndAuth` (OAuth-Popup) ersetzt durch `createInfomaniakConnection` + `submitInfomaniakToken`; `ConnectionsPage` zeigt PAT-Modal mit Schritt-Anleitung + Deeplink zu `manager.infomaniak.com/v3/ng/accounts/token/list`; Cancel rollt PENDING-Connection per DELETE zurueck. (g) Doku: `wiki/d-guides/infomaniak-oauth-setup.md` geloescht, `wiki/d-guides/infomaniak-token-setup.md` neu. (c-work: `c-work/2-build/2026-04-infomaniak-connector.md`)
- 2026-04-28 | refactor | gateway | **Cleanup der zwei tieferliegenden Defensive-Programming-Schichten, die den Trustee-Bug (vorheriger Eintrag) ueberhaupt erst durchgelassen haben.**
- **(1) DB-Connector: fail-loud statt swallow.** `connectorDbPostgre.getRecord/getRecordset/getRecordsetPaginated/getDistinctColumnValues/_loadTable/semanticSearch` haben bisher jede Exception per `except Exception → log → return []` (bzw. `None`/leeres Pagination-Resultat) verschluckt. Folge: jeder echte DB-Fehler (Postgres-Adapt, UndefinedTable, UndefinedColumn, OperationalError, etc.) wurde fuer den Caller ununterscheidbar von "0 Rows" -- darauf basierten misleading downstream Errors wie "No active accounting configuration found". Neu: typisierte Exception `DatabaseQueryError(table, message, original)` plus zentrales `_rollbackQuietly(connection)` (Postgres setzt die Connection in Error-State nach jedem fehlgeschlagenen Statement). Empty Result Sets liefern weiterhin `[]`/`None`/`{items: [], totalItems: 0, totalPages: 0}` (= Normalpfad ueber `cursor.fetchall()/fetchone()`), aber jede Exception innerhalb des Query-Pfads wird hochgereicht. Tests: `tests/unit/connectors/test_connectorDbPostgre_failLoud.py` (9 Tests).
- **(2) Action-Parameter: zentrale Validierung statt impliziter Kontrakt.** Workflow-Actions haben `parameters: Dict[str, Any]` ohne Schema-Enforcement bekommen; die Aktionsimplementationen mussten ad-hoc `isinstance`-Branches haben oder mit Postgres-Errors abstuerzen (siehe Trustee-Bug). Neues Modul `gateway/modules/workflows/processing/shared/parameterValidation.py` mit `InvalidActionParameterError(ValueError)` + `validateAndCoerceParameters(actionDef, parameters)`. Zentral aufgerufen in `ActionExecutor.executeAction` -- gilt fuer alle Aufrufpfade (Agent, Workflow-Graph, REST). Logik:
1. Required-Parameter erzwungen (typisierter Fehler statt opaque downstream).
2. Ref-Schemas (`FeatureInstanceRef`/`ConnectionRef`/...) -- Dict mit `id` wird auf String-UUID kollabiert; Pass-Through fuer bereits-Strings; Dict ohne `id` raisst kontrolliert.
3. Primitive-Coercion (`bool`/`int`/`float`) aus haeufigen String-Formen ("true"/"12"/"3.14"). Unbekannte Extra-Keys (`parentOperationId`, `expectedDocumentFormats`, ...) bleiben unangetastet. Die in der vorherigen Iteration in `actionToolAdapter` eingebaute Ref-Normalisierung wurde komplett entfernt (Single Source of Truth in `parameterValidation`). Tests: `tests/unit/workflows/test_parameterValidation.py` (19 Tests).
- Gesamttest-Suite: 512 passed (vorher 503, plus 9 neue DB-Tests; +19 neue Validation-Tests; -7 obsolete Adapter-Tests, da Logik umgezogen).
- 2026-04-28 | fix | gateway | Agent-Tool-Calls auf Workflow-Actions mit `*Ref`-Parametern (z.B. `trustee_refreshAccountingData(featureInstanceId=...)`) brachen mit irrefuehrendem "No active accounting configuration found" ab. Root cause: das Tool-Schema (Phase-3 Typed Action Architecture) exponiert `FeatureInstanceRef`/`ConnectionRef` absichtlich als typisiertes Objekt mit `id`+Diskriminator (`featureCode`/`authority`), damit der LLM bei mehreren Instanzen die richtige picken kann -- aber die Action-Implementierungen verwenden den Wert direkt als String-UUID in `recordFilter={"featureInstanceId": <value>, ...}`. Der LLM uebergibt korrekt das Dict `{id, featureCode, label, mandateId}`, Postgres-Adapter scheitert mit `can't adapt type 'dict'`, der DB-Connector swallowed den Fehler (soft-fail mit `[]`), und die Action interpretiert das leere Resultset als "Konfiguration fehlt". Initial-Fix in `actionToolAdapter` (Ref-Dict -> id-String). **Diese Adapter-Normalisierung wurde im Folgeschritt zur zentralen `parameterValidation` migriert (siehe naechster Eintrag).**
## 2026-04-27
- 2026-04-27 | fix | gateway | Trustee-Subagent gab fuer "Banksaldo per 31.12.2025" CHF 11'861'162.50 zurueck statt der echten 48'507.41 (Konto 1020) und identifizierte 5400 (Materialaufwand) + 3434 (Erloeskorrekturen) als "Bankkonten". Root cause war doppelt: (1) `closingBalance` ist bereits ein Saldo pro Periode -- der Agent rief `aggregateTable(SUM, closingBalance, GROUP BY accountNumber)` ohne periodYear/periodMonth-Filter und summierte 7 Jahre x 13 Perioden auf (~90x echter Saldo); (2) er hatte kein Wissen darueber, dass Schweizer KMU-Bankkonten dem Praefix `102x` folgen und `periodMonth=0` das Jahres-Total bedeutet. Fix in zwei Teilen: (a) generische Regel in `featureDataAgent._buildSchemaContext` System-Prompt -- "NEVER apply SUM/AVG to columns that already represent a balance, closing/opening total or aggregate" mit konkreten Beispielen `closingBalance/openingBalance/debitTotal/creditTotal`; (b) Pro-Feature-Hook `getAgentDomainHints() -> str` in `mainXxx.py`: wenn die Funktion existiert, wird ihr Rueckgabetext ans Ende des Subagent-Prompts angehaengt. Trustee liefert jetzt einen kompakten Domain-Guide mit KMU-Kontoplan-Praefixen (1xxx/2xxx/3xxx/4xxx, 100x/102x), Periodenkonvention (`periodMonth=0` = Jahr, 1-12 = Monat), drei kanonischen Query-Patterns (Banksaldo, Konto-Saldo, Buchungen-im-Monat) und einer Anti-Pattern-Liste (kein SUM auf closingBalance, debitTotal/creditTotal sind keine Salden). Loader in `_loadFeatureDomainHints` nutzt `loadFeatureMainModules()` und ist tolerant gegen fehlende Hooks/Module. Unit-Tests in `tests/unit/services/test_featureDataAgent_schema.py` (4 neue: Generische Regel, Trustee-Hints angehaengt, kein Hints-Block fuer Features ohne Hook, Anti-Pattern-Erwaehnungen)
## 2026-04-26
- 2026-04-26 | fix | gateway+frontend | Automation Workflow-Tab: `Automation2WorkflowView` erstellt damit sysCreatedAt (timestamp) und lastStartedAt korrekt als PeriodPicker-Spalten erkannt werden; lastStartedAt nutzt jetzt AutoRun.startedAt statt sysCreatedAt; computed-field Filter/Sort via applyFiltersAndSort in-memory; Runs-Tab auf startedAt/completedAt umgestellt statt System-Audit-Felder
- 2026-04-26 | perf | gateway | routeWorkflowDashboard get_system_workflows: N+1 AutoRun-Abfragen ersetzt durch LEFT JOIN + Subquery-Aggregation (eine Daten- + eine Count-Query); FK-Sort-Pfad nutzt eine gebündelte Run-Stats-Query; lastStartedAt/runCount/isRunning-Filter im Join-Pfad in SQL
- 2026-04-26 | fix | gateway | Feature-Data-Subagent (`queryFeatureInstance`) Schema-Prompt war zu duenn: Agent erhielt nur Tabellennamen + flache Spalten-Liste, ohne Typen / Beschreibungen / FK-Beziehungen. Folge: bei Trustee-Saldo-Queries summierte er `TrusteeDataJournalLine.debitAmount/creditAmount` ohne Datumsfilter (JournalLine hat gar kein `bookingDate`!) statt die Periode-bezogenen `TrusteeDataAccountBalance.closingBalance` zu nutzen; ISO-Date-Strings wurden gegen Float-Unix-Timestamps gefiltert (`bookingDate <= '2025-12-31'`). Fix in `featureDataAgent._buildSchemaContext`: pro Tabelle wird jetzt der zugehoerige Pydantic-Klasse via `MODEL_REGISTRY` resolved und pro selektiertem Feld eine angereicherte Zeile gerendert -- Python-Typ aus `field.annotation`, deutsches Label aus `json_schema_extra.label`, Description aus `Field(description=...)`, FK-Target aus `fk_target`. Dazu drei neue Regeln im System-Prompt: (a) Float-Felder mit "unix timestamp" in der Description sind Sekunden-seit-Epoch (Beispiel `'2025-12-31' -> 1735603200.0`), (b) Tools koennen nicht JOINen -- FK-Tabellen separat abfragen, (c) Periode-aggregierte Tabellen (Opening/Closing-Balances) bevorzugt vor SUM ueber Rohdaten. Fallback auf flache Feldliste wenn die Tabelle nicht im Pydantic-Registry ist. Unit-Tests in `tests/unit/services/test_featureDataAgent_schema.py`
- 2026-04-26 | feat | gateway+frontend-nyla | Infomaniak-Connector (kDrive + Mail) als neuer ProviderConnector analog Google/MSFT/ClickUp. Backend: `AuthAuthority.INFOMANIAK`, `providerInfomaniak/connectorInfomaniak.py` mit `KdriveAdapter`+`MailAdapter` (httpx, OAuth-Bearer, Path-Konvention `/{driveId}/{fileId}` bzw. `/{mailboxId}/{folderId}/{uid}`), `routeSecurityInfomaniak.py` mit `_FLOW_CONNECT`-only (kein Login -- pure DATA_CONNECTION), Token-Refresh via `tokenManager.refreshInfomaniakToken` + `tokenRefreshService._refresh_infomaniak_token`, `connectorResolver`-Registry-Eintrag, Authority-Map/Labels/Dispatch in `routeDataConnections`, Router-Mount in `app.py`. Refresh-Token-Persistierung holt bei fehlendem `refresh_token`-Response aus dem letzten gespeicherten Token (analog Google). Frontend: `connectionApi.ts` Authority-Typ erweitert, `useConnections.createInfomaniakConnectionAndAuth` + `infomaniak_connection_success/error`-Event-Listener, `ConnectionsPage` mit Infomaniak-Button (FaCloud), `SourcesTab` Icons/Colors/Service-Mapping fuer `infomaniak`/`kdrive`/`mail` -- inkl. Fix der bisher fehlenden ClickUp-Eintraege in `_SERVICE_ICONS` + `_SERVICE_TO_SOURCE_TYPE`. Setup-Guide unter `wiki/d-guides/infomaniak-oauth-setup.md` (c-work: c-work/2-build/2026-04-infomaniak-connector.md)
- 2026-04-26 | fix | gateway | Feature-Data-Subagent (`queryFeatureInstance`) hat seine Loop hardcoded auf 8 Runden begrenzt, unabhaengig vom Workspace `maxAgentRounds`. Im int-System brachen Trustee-Saldo-Queries deshalb mit `Maximum rounds reached. Progress after 8 rounds` ab, obwohl der Parent-Agent z.B. mit 25 Runden konfiguriert war. Fix: `agentLoop._executeToolCalls` propagiert jetzt `parentMaxRounds` + `parentMaxCostCHF` ueber den Tool-Context; `_featureSubAgentTools._queryFeatureInstance` liest sie aus und reicht sie an `runFeatureDataAgent(maxRounds=, maxCostCHF=)` weiter. Default fuer Direktaufrufer/Tests bleibt 8. Cost-Cap skaliert linear (`_MAX_COST_CHF_PER_ROUND = 0.02 * maxRounds`), damit nicht der 0.15-CHF-Guard die Loop vor Erreichen der konfigurierten Runden abschiesst (25 Runden -> 0.50 CHF). Subagent-Start loggt jetzt effektive `maxRounds`/`maxCostCHF` zur Diagnose
- 2026-04-26 | feat | gateway+frontend-nyla | Database-Health Orphan-Cleanup: neue Checkbox `Ohne FK-Referenzen zu UserInDB.id` (default ON). Deleted-User-Reste in Audit/Billing/Membership-Tabellen sammeln sich natuerlich an wenn ein User geloescht wird und gehoeren in den separaten User-Purge-Workflow, nicht in die generische FK-Bereinigung. Backend: `_isUserIdFk(targetTable, targetColumn)`-Helper (case-insensitive auf Tabellenname); `_cleanAllOrphans(force, excludeUserFks)` ueberspringt entsprechende Relationen; `/orphans?excludeUserFks=true` filtert Scan-Resultate; `OrphanCleanAllRequest.excludeUserFks` filtert clean-all. Frontend: Checkbox neben `Nur Probleme`, default checked, mit Tooltip; URL-Param + clean-all Body-Field synchron; `Alle bereinigen`-Counter zeigt jetzt nur Non-User-FK-Orphans
- 2026-04-26 | fix | gateway | aicorePluginOpenai: `max_tokens` durch `max_completion_tokens` ersetzt in `callAiBasic` und `callAiBasicStream`. Hintergrund: OpenAI lehnt `max_tokens` fuer gpt-5.x / o-series Modelle mit HTTP 400 `unsupported_parameter` ab (`Use 'max_completion_tokens' instead`). Im Log `log_app_20260426.log` (L741-764) sichtbar: `gpt-5.4-nano` failover scheiterte sofort, ModelSelector wechselte auf `claude-opus-4-6`. Per OpenAI API-Reference akzeptieren ALLE aktuellen Chat-Completions-Modelle (legacy gpt-4o/gpt-4.1, gpt-5.x, o1/o3/o4) `max_completion_tokens`, daher universeller Wechsel statt Modell-spezifischer Verzweigung
- 2026-04-26 | feat | gateway | PDF-Renderer Emoji-Support: Noto Emoji (monochrome, OFL) als Fallback-Font registriert. Bisher rendern WinAnsi-Core-Fonts (Helvetica/Courier) Emoji-Codepoints (U+2600+, U+1F300+) als fehlende Glyphen-Quadrate. Neu unter `gateway/assets/fonts/NotoEmoji-Regular.ttf` (~419 KB, 887 Codepoints) + `_pdfFontFallback.py` Helper: registriert die TTF einmalig bei reportlab, scannt deren cmap, und `wrapEmojiSpansInXml` umschliesst zusammenhaengende Emoji-Runs (codepoint >= U+2000 ∧ in cmap) mit `<font name="NotoEmoji">…</font>` — nestet sauber in `<b>`/`<i>`/`<font name="Courier">`. `rendererPdf._markdownInlineToReportlabXml` wendet das am Ende an, also greift es ueberall wo Paragraph-Markup gebaut wird (Headings, Paragraphs, Bullet-Lists, Table-Cells, extracted_text). `Preformatted` (Code-Blocks) ist Single-Font-only und bleibt unveraendert — Emojis in Code-Bloecken sind selten, Box-Drawing wird wie bisher zu ASCII normalisiert. Smoke-Test in `test_renderer_pdf_smoke.py`
- 2026-04-26 | fix | gateway | FK Orphan-Scanner loeschte korrekte Trustee-Workflows: `AutoWorkflow.templateSourceId` enthaelt teils Sentinel-IDs (z.B. `"trustee-receipt-import"` aus `featureModule.getTemplateWorkflows()`), die absichtlich keine DB-Zeile haben — wurden faelschlich als Orphans markiert und mit `force=true` (oder unter 50%-Schwelle) geloescht. Neuer `softFk: True` Flag in `fk_target`: `fkRegistry.FkRelationship` traegt das Flag, `databaseHealth._scanOrphans`/`_cleanOrphans`/`_listOrphans` ueberspringen soft FKs komplett (kein Display, kein Cleanup). Label-Resolution unveraendert. `templateSourceId` als `softFk: True` markiert. Wiki `b-reference/platform/database-architecture.md` aktualisiert
- 2026-04-26 | refactor | gateway+frontend-nyla | Letzte 4 hardcoded Cell-Label-Stellen entfernt: (1) `RoleView.scopeType` (select mit `frontend_options` System-Template/Template/Mandant) + `RoleView.userCount` -> `AdminMandateRolesPage` zieht Attribute jetzt von `RoleView`, kein lokaler `scopeType`-Formatter mehr; (2) `Invitation.expiredFlag` als Pydantic `@computed_field` (live aus `expiresAt`+`time.time()`) mit `frontend_format_labels=["Ja","-","Nein"]`; (3) `Invitation.emailSent` -> `emailSentFlag` umbenannt + neues `emailSentAt`-Feld (Persistenz im DB-Record), `routeInvitations.create_invitation` setzt beide nach erfolgreichem Mailversand; (4) `TrusteePositionView` mit `syncStatus` (select Ausstehend/Synchronisiert/Fehler/Abgebrochen) + `syncErrorMessage` -> `routeFeatureTrustee.get_positions` enriched Rows aus `TrusteeAccountingSync`, `useTrustee` lookt Attribute via neuem `attributesEntityName`-Override, `TrusteePositionsView` hat eigenen Sync-State + Custom-Renderer geloescht. `attributeUtils.getModelAttributeDefinitions` und `i18nRegistry.@i18nModel` verarbeiten jetzt auch `model_computed_fields` (Labels + `frontend_format_labels` werden registriert)
- 2026-04-26 | refactor | gateway+frontend-nyla | Hardcoded Cell-Labels aus FormGeneratorTable-Pages entfernt: Boolean-Formatter ("Ja"/"Nein", "OK"/"Fehler") und Enum-Maps (`_STATUS_LABELS`, `scopeLabels`) aus `GraphicalEditorWorkflowsPage`, `GraphicalEditorTemplatesPage`, `AutomationsDashboardPage`, `ComplianceAuditPage` ersatzlos geloescht. Stattdessen Pydantic-Modelle (`AutoWorkflow.active|sharedReadOnly|isTemplate|notifyOnFailure|templateScope`, `AutoRun.status`, `AutoStep.status`, `AutoTask.status`, `AutoVersion.status`, `Automation2WorkflowView.isRunning`, `AuditLogEntry.success`) mit `frontend_format_labels`/`frontend_options` ausgestattet. `resolveColumnTypes` merged jetzt auch `label` und `options` aus dem Backend; `FormGeneratorTable` rendert Cells UND Filter-Dropdowns ueber `column.options` automatisch — Pages duerfen Labels nicht mehr im Frontend hardcoden
- 2026-04-26 | feat | frontend-nyla | FormGeneratorTable + columnTypeResolver: `ColumnConfig.options` (aus `frontend_options` der Pydantic-Felder) ist jetzt erste Klasse; Cell-Renderer und Filter-Liste resolven Value -> Label automatisch; `column.label` ist optional und wird vom Backend-Attribut gefuellt
- 2026-04-26 | fix | gateway+frontend-nyla | GraphicalEditor Workflows-Tabelle: `createdAt`-Alias aus `routeFeatureGraphicalEditor.get_workflows` entfernt — Frontend nutzt nun das kanonische `sysCreatedAt`. `GraphicalEditorWorkflowsPage` + `GraphicalEditorTemplatesPage` holen Attribute jetzt von `Automation2WorkflowView` (mit `frontend_type=timestamp` fuer `sysCreatedAt`/`lastStartedAt` und `frontend_type=number` fuer `runCount`); Spalten haben explizit `sortable`/`filterable` gesetzt — fehlende Sort-Icons und Zahl-statt-PeriodPicker behoben. `Automation2Workflow`-TS-Interface auf `sysCreatedAt` umgestellt
- 2026-04-26 | refactor | frontend-nyla | RealEstate Parcels+Projects + GraphicalEditor Workflows+Templates: `apiEndpoint` auf den jeweiligen Listenroute gesetzt — Backend-Routen unterstuetzen `mode=filterValues&column=X` und `mode=ids` ueber `handleFilterValuesInMemory`/`handleIdsInMemory`; FormGeneratorTable holt Filter-Werte jetzt sauber vom Backend (kein Local-Mode mehr noetig)
- 2026-04-26 | feat | gateway | routeFeatureGraphicalEditor: `/workflows` und `/templates` Endpunkte unterstuetzen jetzt `mode=filterValues&column=X` und `mode=ids` (FormGeneratorTable Backend-Pattern) ueber `handleFilterValuesInMemory`/`handleIdsInMemory` aus routeHelpers
- 2026-04-26 | fix | frontend-nyla | AdminLanguagesPage: `hookData.fetchFilterValues` implementiert (offizielles Pattern fuer In-Memory-Tabellen ohne Backend-Endpunkt) — distinct Filter-Werte aus `displayRows` mit Cross-Filter-Support; ersetzt das zuvor versuchte FormGeneratorTable-Local-Mode
- 2026-04-26 | revert | frontend-nyla | FormGeneratorTable: Local-Mode-Fallback in `getUniqueValuesForColumn` und das Entfernen der console.warn rueckgaengig gemacht — silent Fallbacks verstossen gegen das Prinzip "klare Datenstrukturen + Modelle im Backend"; Tabellen muessen stattdessen `apiEndpoint` (Backend) oder `hookData.fetchFilterValues` (explizit) setzen
- 2026-04-26 | fix | frontend-nyla | AutomationsDashboardPage Workflows-Tab: Spalten `isRunning` und `runCount` als `sortable` + `filterable` markiert (Backend unterstuetzt JOIN-basierte Sortierung/Filterung dieser computed fields)
- 2026-04-26 | fix | gateway | Automation2 ExecutionEngine: `AutoRun.startedAt` wird jetzt in `createRun` gesetzt; `AutoRun.completedAt` wird in `updateRun` automatisch gesetzt sobald Status terminal wird (completed/failed/stopped/cancelled); routeWorkflowDashboard.stopRun setzt `completedAt` ebenfalls. Bisher wurden diese Felder nie befuellt — daher waren `started`/`completed` Spalten in der Runs-Tabelle leer
- 2026-04-26 | fix | frontend-nyla | PeriodPicker in FormGeneratorTable: Preset-Kind (`thisMonth`, `thisQuarter` etc.) wird im Filter-Wert mitgespeichert, damit es beim Round-Trip erhalten bleibt und `isValueAllowed` nicht faelschlicherweise auf `ytd` zurueckfaellt
- 2026-04-26 | fix | gateway | routeAudit: 500-Fehler bei Datumsfilter behoben — `PaginationParams(pageSize=999999)` verletzte `le=1000`-Constraint; nutzt jetzt `model_construct` + `SortField`-Konvertierung
- 2026-04-26 | refactor | gateway | 5 Pattern-Inkonsistenzen aus FormGeneratorTable-Audit behoben: routeDataUsers stiller Fallback entfernt; routeFeatureRealEstate Projekte+Parzellen nutzen jetzt `applyFiltersAndSort` statt nur Sorting; routeAdminRbacRules custom filter/sort durch shared Helper ersetzt + `enrichRowsWithFkLabels` ergaenzt
- 2026-04-26 | fix | frontend-nyla | ComplianceAuditPage: Fallback-Formatter fuer `instanceLabel` und `username` zeigen jetzt `NA(uuid)` statt abgeschnittener UUID ohne Kontext
- 2026-04-26 | fix | gateway | aicoreModelRegistry: Race-Condition in `refreshModels` behoben — Lock verhindert konkurrierende Refreshes; harmlose Duplikate (gleicher Name+Connector) werden toleriert statt als Fehler geworfen
- 2026-04-26 | fix | gateway | routeDataFiles: `mode=filterValues` nutzt jetzt `enrichRowsWithFkLabels` + `handleFilterValuesInMemory` statt direktem `getDistinctColumnValues` — FK-Spalten (mandateId, featureInstanceId) zeigen wieder Labels statt UUIDs
- 2026-04-26 | fix | gateway | routeFeatureTrustee: 3x `mode=filterValues` (Documents, Positions, generisch) von `getDistinctColumnValuesWithRBAC` auf `enrichRowsWithFkLabels` + `handleFilterValuesInMemory` umgestellt — FK-Spalten (organisationId, roleId, userId, contractId etc.) zeigen Labels statt UUIDs; generischer Endpunkt nutzt zusaetzlich `_buildFeatureInternalResolvers` fuer Feature-interne FKs
- 2026-04-26 | fix | gateway | routeAudit: `_enrichUserAndInstanceLabels` setzt jetzt `NA(uuid)` als Fallback statt `None` fuer nicht aufloesbare FeatureInstance/User-IDs — Filter-Dropdown fuer Feature-Instanz war leer weil alle Labels `None` waren
- 2026-04-26 | fix | gateway | Zwei Filter-Bugs: (1) `applyFiltersAndSort` in routeHelpers: `value is None` filtert jetzt auf leere Felder statt den Filter zu ueberspringen ("Leer"-Option funktioniert); (2) `routeAudit._applySortFilterSearch` durch Delegation an shared `applyFiltersAndSort` ersetzt — Datumsbereich-Filter (`between`-Operator) und Null-Filter funktionieren jetzt konsistent
- 2026-04-26 | fix | gateway | Stille `except Exception`-Fallbacks in `mode=filterValues` entfernt: `routeFeatureTrustee` (_handleDocumentMode, _handlePositionMode, _paginatedReadEndpoint), `routeDataFiles`, `routeDataMandates` — Fehler bubblen jetzt hoch statt stillschweigend auf teuren In-Memory-Pfad auszuweichen
- 2026-04-26 | fix | gateway | Filter-Dropdown-UUID-Bug: `enrichRowsWithFkLabels` fehlte im `mode=filterValues`-Pfad bei 8 Routen (routeDataConnections, routeInvitations, routeAdminFeatures, routeSubscription, routeFeatureRealEstate x2); Wiki `fk-label-resolution.md` mit Filter-Enrichment-Regel fuer AI-Agent ergaenzt
- 2026-04-26 | refactor | gateway | FK-Metadaten konsolidiert: alle Datamodels nutzen `fk_target` mit Pflicht-Keys `db`/`table`/`labelField`; `fk_model`/`fk_label_field` entfernt; `_BUILTIN_FK_RESOLVERS["UserInDB"]`; `_buildLabelResolversFromModel` skip ohne `labelField`; `attributeUtils` setzt `displayField` nur bei gesetztem `labelField`; `validateFkTargets()` Startup-Validierung in `fkRegistry.py` + `app.py lifespan`; Wiki `fk-label-resolution.md` + `database-architecture.md` aktualisiert
- 2026-04-26 | fix | gateway | `connectorDbPostgre._ensureTableExists`: TEXT->DOUBLE PRECISION Spalten-Migration schlug fehl fuer ISO-Datetime-Strings — Regex `\\d{{4}}` korrigiert zu `\\d{4}` (doppelte Klammern waren kein f-string-Escaping sondern literal), `::timestamp` auf `::timestamptz` (Timezone-Offset korrekt parsen), SAVEPOINT pro ALTER (eine fehlgeschlagene Migration killt nicht mehr die gesamte Transaktion) — betraf MandateSubscription (6 Spalten) und BackgroundJob (3 Spalten)
- 2026-04-26 | refactor | frontend-nyla | FlowEditor Form-Field-Type-Zentralisierung: `FORM_FIELD_TYPES` + `FORM_FIELD_TYPE_LABELS` in `attributeTypeMapper.ts`; `FormStartNodeConfig`, `FormNodeConfig`, `FieldBuilderEditor` beziehen Feldtypen aus zentraler Library statt hardcoded Listen; ClickUp-spezifische Typen (`clickup_status`, `clickup_tasks`) und zugehoerige UI (Connection-Picker, Status-Hinweis) entfernt; shared `FormField`-Typ auf `AttributeType` + generisches `options` umgestellt; `TriggerFormFieldRow` eliminiert; `clickupFormSync.ts` geloescht (dead code, nirgends importiert)
- 2026-04-26 | refactor | gateway+frontend-nyla | Column-Type-Refactoring Schritte 1-10 abgeschlossen: 6 Pydantic View-Modelle (`UserMandateView`, `FeatureAccessView`, `BillingTransactionView`, `MandateSubscriptionView`, `UiLanguageSetView`, `DataNeutralizerAttributesView`) in `datamodelViews.py`; `createdAt`/`createdBy` Aliase in Invitation- und Billing-DTOs auf `sysCreatedAt`/`sysCreatedBy` standardisiert; `_COL_MAP`-Remapping in `interfaceDbBilling` entfernt; 7 Admin/Billing/Compliance-Seiten beziehen Spaltentypen via `resolveColumnTypes` + `fetchAttributes('<ViewModelName>')` statt hardcoded `type:` — tsc + Grep-Completeness-Check bestanden
- 2026-04-26 | refactor | frontend-nyla | Schritt 8: Vollstaendigkeits-Grep — verbleibende hardcoded `type:` in ComplianceAuditPage entfernt (username/instanceLabel/ipAddress im Modell); alle anderen type:-Werte berechtigt dokumentiert (enriched View-Spalten, synthetische Zaehler, Alias-Keys)
- 2026-04-26 | refactor | frontend-nyla | Schritt 7: weitere FormGenerator-Tabellen (AdminUsers, Connections, Files/Prompts, Mandate-Hook, RealEstate*, Trustee*) bauen Spaltentypen nur noch via `resolveColumnTypes` statt `type: attr.type` im Column-Map
- 2026-04-26 | feat | gateway | `attributeUtils.getModelClasses`: Feature-`datamodel*.py` unter `modules/features/**` rekursiv importieren (Trustee, Teamsbot, GraphicalEditor, …) fuer `/api/attributes/{entityType}`
- 2026-04-26 | refactor | frontend-nyla | Workflow-Seiten (GraphicalEditorWorkflowsPage, AutomationsDashboardPage) beziehen Spaltentypen via `resolveColumnTypes` + `fetchAttributes` vom Backend statt hardcoded `type:` im Frontend; neuer Shared-Utility `columnTypeResolver.ts`
- 2026-04-26 | feat | frontend-nyla | `attributesApi.AttributeDefinition.type` nutzt `AttributeType` aus `attributeTypeMapper`; Mapper um Backend-Typ `object` (JSON/Dict) ergaenzt
- 2026-04-26 | feat | gateway | Pydantic CHECK-Cleanup: fehlendes `frontend_type: "timestamp"` bei float-Zeitfeldern (UAM resetTokenExpires, DataSource, Security Token, Chat ChatLog/publishedAt/ActionItem/TaskItem/TaskHandover, Knowledge extractedAt); Redmine `*OnTs` von `number` auf `timestamp`; Redmine DTOs (`RedmineSyncResultDto`, `RedmineSyncStatusDto`, `RedmineConfigDto`); `UsageStatistics.periodStart` mit `frontend_type: "date"`; alle `frontend_type: "datetime"` auf `"timestamp"` (Audit, AuthEvent, GraphicalEditor Auto*, Messaging sentAt) — konsistent mit `attributeTypeMapper.isDateTimeType`
- 2026-04-26 | refactor | gateway | Boot-Optimierung: Chatbot-Duplikat-Prewarm entfernt (`routeFeatureChatbot`), Stripe-Bootstrap parallelisiert via ThreadPoolExecutor (`stripeBootstrap`) — erwartete Bootzeit-Reduktion ~8s
- 2026-04-26 | fix | gateway | `interfaceRbac`: Pagination-Dict-Filter (`getRecordsetPaginatedWithRBAC` / `getDistinctColumnValuesWithRBAC`) nutzen `_rbacAppendPaginationDictFilter` — numerische `gt`/`gte`/`lt`/`lte`/`between` mit `::double precision`, ISO-Datum + numerische Spalte als Unix-Bounds wie Connector
- 2026-04-26 | feat | frontend-nyla | `FormGeneratorTable`: Datum-Filter nutzt PeriodPicker (Presets + Kalender) statt primitiver `<input type="date">`; PeriodPicker rendert ausserhalb `filterDropdownOptions` (Popover nicht durch `overflow-y: auto` geclippt)
- 2026-04-26 | fix | gateway | `TrusteeDataJournalEntry.bookingDate`: `Optional[str]` -> `Optional[float]` (unix timestamp); Konvertierung in `_persistJournal` via `_isoDateToTimestamp` (ValueError bei ungueltigem Datum, kein Fallback); `_aggregateLocalMovements` liest float; FK-Label-Resolver formatiert float als ISO; Demo-Daten konvertiert; DB-Migration TEXT->DOUBLE PRECISION in `_ensureTableExists`
- 2026-04-26 | fix | gateway | `datamodelFeatureTrustee`: `lastSyncAt`/`chartCachedAt`/`syncedAt` bekommt `frontend_type: "timestamp"`, `lastSyncDateFrom`/`lastSyncDateTo` `frontend_type: "date"` — damit Frontend Date-Filter statt Text anzeigt
- 2026-04-26 | fix | frontend-nyla | `FormGeneratorTable`: Filter-Dropdown per `useLayoutEffect` als `position: fixed` in den Viewport geklemmt; Audit-Timestamp-Spalten (`sysCreatedAt` etc.) bei numerischem `type` als Datums-UI + kein distinct-Fetch
- 2026-04-26 | feat | frontend-nyla | `FormGeneratorTable`: typbezogene Spaltenfilter — Zahlen (`integer`/`int`/`number`/`float`) mit Operator (=, >, >=, <, <=, Zwischen) und `Anwenden`; Datum-Filter mit CSS-Panel; kein `filterValues`-Fetch mehr fuer bool/date/number-Spalten
- 2026-04-26 | fix | gateway | `routeHelpers._matchesBetween`: numerische `from`/`to` nach fehlgeschlagenem Datums-Parse (korrekte BETWEEN-Logik fuer Zahlenspalten); `connectorDbPostgre`: `gt`/`gte`/`lt`/`lte` und `between` auf INTEGER/DOUBLE PRECISION mit `::double precision` statt lexikographischem TEXT-Vergleich
## 2026-04-25
- 2026-04-25 | feat | * | Phase 4 FK: `frontend_fk_*` und FormGenerator-`fkSource`/Client-Cache entfernt; `fk_label_field` + `displayField` only; `_resolveRoleLabels`; `getRecordsetPaginated` + `getRecordsetPaginatedWithRBAC` + FK-Sort-Pfad mit `_enrichRowsWithFkLabels`; `attributeUtils` + betroffene Datamodels + Pages auf reines Backend-Enrichment
- 2026-04-25 | fix | gateway | Trustee Account Balances: echte Schlusssalden aus Buchhaltungssystem importieren (RMA via `/gl/saldo`; Bexio via Journal-Aggregation; Abacus via OData-Aggregation); korrigierte kumulative Fallback-Berechnung in `_persistBalances`; neues `AccountingPeriodBalance`-Modell + `getAccountBalances`-Methode in `BaseAccountingConnector`; Bug "Banksaldo per Stichtag falsch" (BuHa SoHa Konto 1020) geloest (c-work: c-work/4-done/2026-04-trustee-account-balances-import.md)
- 2026-04-25 | test | gateway | Unit-Tests fuer Trustee-Balance-Import: RMA-Connector (BuHa-SoHa-Szenario + ER-Reset), Bexio-Connector (kumulative Aggregation + Carry-Over), Abacus-Connector (OData-Aggregation), AccountingDataSync (Connector-Path + Local-Fallback)
- 2026-04-25 | feat | gateway | FK-Resolution Phase 2 (A1+A2): Neue zentrale `_enrichRowsWithFkLabels()` in `routeHelpers.py` — bulk-resolved FK-Labels als `{field}Label`-Spalten pro Row; `_resolveMandateLabels`/`_resolveInstanceLabels`/`_resolveUserLabels` liefern `None` statt ID bei fehlender Aufloesung; `routeWorkflowDashboard`, `routeAudit`, `routeBilling` (Transactions + Billing-Aggregation), `routeSubscription` auf zentrale Funktion migriert (`or mid[:8]` / `or uid[:8]` / `or iid`-Fallbacks entfernt)
- 2026-04-25 | feat | gateway | FK-Resolution Phase 2 (B2): `_enrichedFilterValues` in `routeWorkflowDashboard` liefert `{value, label}` Objekte fuer FK-Spalten (mandateId, featureInstanceId) — Frontend zeigt Labels im Filter-Dropdown ohne separate `fkSource`-Aufloesung; Leerwerte (`null`) fuer "(Leer)"-Filter inkludiert
- 2026-04-25 | fix | gateway+frontend | FK-Resolution Korrektur: `routeWorkflowDashboard` runs/workflows-Enrichment benennt `mandateIdLabel``mandateLabel` um (Frontend-Interface-Kompatibilitaet); `AutomationsDashboardPage` Spalten mandateId/featureInstanceId nutzen `displayField: 'mandateLabel'/'instanceLabel'`
- 2026-04-25 | feat | gateway | FK-Resolution Phase 2 (B1): `getDistinctColumnValues` + `getDistinctColumnValuesWithRBAC` + `_extractDistinctValues` + `_distinctColumnValues` liefern `null` als letzten Eintrag wenn NULL/Leer-Zeilen existieren — Frontend kann "(Leer)"-Filter anbieten
- 2026-04-25 | feat | frontend-nyla | FK-Resolution Phase 3 (C1+C2): `FormGeneratorTable.ColumnConfig.displayField` — neues Pattern: Cell rendert `row[displayField]` statt `row[key]`, CSV nutzt `displayField`; `fkSource`/`fkDisplayField` als `@deprecated` markiert (Legacy-Pfad funktioniert weiterhin)
- 2026-04-25 | feat | frontend-nyla | FK-Resolution Phase 3 (B3): `FilterValuesList` akzeptiert `string | null | {value, label}` Eintraege; `FilterValue`-Typ eingefuehrt; `_normalizeFilterValue` normalisiert alle 3 Formate; Backend-`null`-Eintraege werden als "(Leer)"-Option gerendert
- 2026-04-25 | fix | gateway | Fallback-Cleanup Phase 1 (D1+D2): Pagination-Parsing in `routeWorkflowDashboard` (runs/workflows) und `routeDataMandates` wirft 400 bei kaputtem JSON statt silent default; `runsByStatus`/Run-Enrichment in `/metrics` + `/workflows` propagieren DB-Fehler statt `logger.warning`+200; `delete_system_workflow` Callback-Trigger meldet Listener-Bugs (500 statt `except: pass`); `routeBilling._isAdminOfMandate`/`_isMemberOfMandate` und `routeSubscription._assertMandateAdmin` fail-loud (kein "DB-Down → 403"-Mask mehr); Stripe `Subscription.retrieve` im Checkout-Webhook re-raised statt silent skip
- 2026-04-25 | fix | gateway | Fallback-Cleanup Phase 1 (D2): `routeInvitations` Rollen-Zuweisung — `addRoleToFeatureAccess`/`addRoleToUserMandate` sind bereits idempotent, daher `try/except: pass # Role might already be assigned` entfernt → echte FK-/DB-Fehler beim Einladungs-Akzept werden jetzt sichtbar
- 2026-04-25 | fix | frontend-nyla | Fallback-Cleanup Phase 1 (D3+D4): `AutomationsDashboardPage._handleExecute` zeigt "Workflow gestartet" nur noch, wenn die 1s-Beobachtungs-Phase weder Erfolg noch Fehler beobachtet hat (kein Doppel-Toast "gestartet" + "fehlgeschlagen" mehr); `_loadMetrics` toast-t Backend-Fehler statt nur `console.error`; `Automation2FlowEditor.handleWorkflowRename` zeigt Fehler-Toast statt unsichtbarem `console.error`
- 2026-04-25 | feat | frontend-nyla | `FormGeneratorTable`: Leerwert-Filter `(Leer)` in allen Filter-Dropdowns — filtert auf `IS NULL OR = ''` (Backend unterstützt bereits `null` in Pagination-Filtern); Filter-Icon/Clear-Button erkennen `null`-Filter korrekt via `key in filters`
- 2026-04-25 | fix | frontend-nyla | NodeConfigPanel/RequiredAttributePicker/FeatureInstancePicker: Texte (Type-Badges, Bound-Refs, Vorschlag-Labels, Beschreibungen) verlassen den 280px-Panel-Frame nicht mehr — Header-Layout `label flex:1 1 100 %` lässt Badge umbrechen; `box-sizing: border-box`, `overflow-x: hidden`, `overflow-wrap: anywhere` als Safety-Net auf `.nodeConfigPanel`; Bound-Chip/Vorschlag-Button mit `whitespace: normal` + `word-break`
- 2026-04-25 | fix | frontend-nyla | KeepAlive-Wrapper (`GraphicalEditor`, `Workspace`, `Commcoach`): Persistenz strikt pro `(mandateId, instanceId)``key={mandate:instance}` an die gehaltene Page; Wechsel der Mandanten-/Instanz-Tupel unmountet den alten Editor (kein Cross-Tenant-Save mehr, "not found"-Bug behoben); Unit-Test `GraphicalEditorKeepAlive.test.tsx`
- 2026-04-25 | fix | frontend-nyla | `DataPicker`: per `createPortal` nach `document.body` (entkoppelt von `.nodeConfigPanel button`-Primary-Override); neues List-Row-Layout/Theme (`dataPickerNodeHeader` neutral), Header-Badge/Filter/Close-Styles, höheres z-index
- 2026-04-25 | fix | frontend-nyla | Flow-Editor `CanvasHeader`: Zwei-Spalten-Layout (Kontext: fester Workflow-Dropdown + Titel mit Ellipsis | Aktionspanel); Run-Button `min-width`; Version-Zeile getrennt; `retryButton`-Margin im Toolbar-Panel neutralisiert
- 2026-04-25 | fix | gateway | Trustee-Template `trustee-receipt-import`: `documentList` als DataRef `extract→process→sync` (Pick-not-Push), nicht leere Listen; Unit-Test `test_trustee_template_workflows.py`
- 2026-04-25 | feat | gateway | Trustee + Redmine Nodes auf typisierten `FeatureInstanceRef[<code>]`-Param + `frontendType: featureInstance` migriert (c-work: c-work/4-done/2026-04-feature-instance-ref-adapter-migration.md)
- 2026-04-25 | feat | gateway | Neuer Endpoint `GET /api/workflows/{instanceId}/options/feature.instance?featureCode=…` fuer Mandanten-gefilterte FeatureInstance-Auswahl (c-work: c-work/4-done/2026-04-feature-instance-ref-adapter-migration.md)
- 2026-04-25 | feat | frontend-nyla | `FeatureInstancePicker` (0/1/N) als Renderer fuer `frontendType: featureInstance`; Sysadmin-Toggle "Schema-Details" im CanvasHeader (c-work: c-work/4-done/2026-04-feature-instance-ref-adapter-migration.md)
- 2026-04-25 | fix | frontend-nyla | NodeConfigPanel-Banner zeigt `param.name` statt der ausschweifenden Description (Tooltip enthaelt vollen Text); hidden-Pflicht-Params werden zentral in `findRequiredErrors` gefiltert (kein Phantom-Pflichtfeld mehr) (c-work: c-work/4-done/2026-04-feature-instance-ref-adapter-migration.md)
- 2026-04-25 | fix | frontend-nyla | DataPicker-Modal auf CSS-Variablen umgestellt; Hover-Safety-Net `dataPickerLeaf:hover *` haelt Type-Hints auf blauem Hintergrund lesbar (c-work: c-work/4-done/2026-04-feature-instance-ref-adapter-migration.md)
- 2026-04-25 | docs | wiki | Audit `2026-04-node-typization-audit.md` archiviert; Folge-Track-Doc `2026-04-feature-instance-ref-adapter-migration.md` direkt in `4-done/` als erledigt
- 2026-04-25 | docs | wiki | Changelog-Konvention im `_CHANGELOG.md` eingefuehrt; in `README.md` + `doc-sync.mdc` referenziert