# Changelog (c-work) Eine Zeile pro Change, NEUESTE EINTRAEGE ZUOBERST. Begruendungen gehoeren ins zugehoerige `c-work//.md` oder die PR-Beschreibung. Format: `- YYYY-MM-DD | | | [(c-work: )] [(PR: #123)]` type: `feat` `fix` `refactor` `docs` `test` `chore` `build` · scope: `gateway` `frontend-nyla` `private-llm` `teams-bot` `wiki` `infra` `*` Skip: reine Refactors, Formatting, Lint, Dep-Bumps, Test-only, Wiki-Tippfehler. ## 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 | 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 `